Skip to main content

tatara_process/
patch.rs

1//! Substrate primitive for the merge-patch idiom over the `/status`
2//! subresource of any kube [`Resource`].
3//!
4//! Owns the 2-link chain
5//!
6//! ```text
7//! let body = json!({ "status": <typed> });
8//! api.patch_status(name, &PatchParams::default(), &Patch::Merge(&body)).await
9//! ```
10//!
11//! that every controller-side writer hand-authored pre-lift at each
12//! phase-transition + observed-fanout site.
13//!
14//! Sibling to the SSA-side substrate primitive
15//! [`crate::api_version`]-adjacent `tatara_reconciler::ssapply::apply_patch_params`
16//! (which owns the `PatchParams::apply(<mgr>).force()` peer on the
17//! server-side-apply axis). Together, the two primitives own the two
18//! wire-side write-posture axes the workspace's controllers stamp:
19//!
20//! - `Patch::Merge + PatchParams::default()` — status-subresource
21//!   writes, applied here by every phase-transition writer in the
22//!   `tatara-pool-reconciler` (allocation controller, pool controller)
23//!   and the `tatara-reconciler` (Process status writer).
24//! - `Patch::Apply + PatchParams::apply(<mgr>).force()` — rendered
25//!   FluxCD resource applies + `RELEASED_FROM` marker + the
26//!   `ProcessTable.status.claims` writer.
27//!
28//! ### Return type + `#[must_use]`
29//!
30//! Returns the reconstructed `K` on success — matches `Api::patch_status`
31//! verbatim. Pool + Process controllers today discard the returned `K`
32//! (`let _ = merge_status(...).await;` after `AllocationDecision` /
33//! phase-transition branches), but the primitive keeps the return in
34//! the signature so a future writer that needs the reconciled
35//! resource-version / observed-generation from the same wire round-trip
36//! doesn't have to re-fetch. `#[must_use]` on the returned `Future`
37//! keeps a caller from building the patch call and dropping it
38//! un-awaited — the same silent-drop defect the pre-lift free-chain
39//! form quietly permitted.
40
41use kube::api::{Api, Patch, PatchParams};
42use kube::Resource;
43use serde::{de::DeserializeOwned, Serialize};
44use serde_json::json;
45use std::fmt::Debug;
46
47/// Compose the merge-patch wire body `{"status": <status>}` — the
48/// pure step [`merge_status`] performs before handing off to
49/// `Api::patch_status`.
50///
51/// Extracted as a standalone helper so the wire-body shape can be
52/// pinned by fail-before-pass-after tests without a live kube client
53/// or tokio reactor. A regression that drifts the top-level slot name
54/// (a `"Status": …` case-fold, a `"status_patch": …` verbose rename,
55/// an accidental array-wrap) surfaces here at every invariant pin
56/// rather than as silent operator-facing drift at each downstream
57/// consumer.
58#[must_use]
59pub fn merge_status_body<S: Serialize + ?Sized>(status: &S) -> serde_json::Value {
60    json!({ "status": status })
61}
62
63/// Merge-patch the `/status` subresource of any kube [`Resource`] with
64/// a typed `status` value.
65///
66/// Owns the 2-step wire-side chain `merge_status_body(status) →
67/// Api::patch_status(name, PatchParams::default(), Patch::Merge)` at
68/// ONE substrate owner across every workspace controller. Pre-lift the
69/// chain recurred at 7 hand-authored sites (4 in
70/// `tatara-pool-reconciler::controller_allocation`, 2 in
71/// `tatara-pool-reconciler::controller_pool`, 1 wrapped inside
72/// `tatara-reconciler::patch::patch_process_status`) past the ★★
73/// PRIME-DIRECTIVE ≥ 2 duplication trigger.
74///
75/// A future normalization of the merge-patch posture (an injectable
76/// field manager for status writes, a strategic-merge escape hatch, a
77/// dry-run gate for one-shot dry-runs, an added `resourceVersion`
78/// precondition slot) lands at THIS ONE function and every downstream
79/// consumer inherits the upgrade mechanically.
80pub async fn merge_status<K, S>(api: &Api<K>, name: &str, status: &S) -> Result<K, kube::Error>
81where
82    K: Resource + DeserializeOwned + Clone + Debug,
83    K::DynamicType: Default,
84    S: Serialize + ?Sized,
85{
86    let body = merge_status_body(status);
87    api.patch_status(name, &PatchParams::default(), &Patch::Merge(&body))
88        .await
89}
90
91/// Merge-patch the PRIMARY resource endpoint of any kube [`Resource`]
92/// with a caller-composed wire body.
93///
94/// Primary-resource sibling to [`merge_status`] on the (wire-endpoint ×
95/// wrap-posture) pair: [`merge_status`] owns the `/status` subresource
96/// axis (`api.patch_status(...)`) AND wraps the caller's typed value
97/// into `{"status": <typed>}` before dispatching; this primitive owns
98/// the primary-resource axis (`api.patch(...)`) and passes the caller's
99/// body through verbatim — the caller composes the top-level `spec:`,
100/// `metadata:`, `data:`, or other merge-patch slot before hand-off.
101///
102/// The wrap asymmetry between the two primitives matches the pre-lift
103/// callsite discipline exactly: every `/status` writer built a typed
104/// status value (an `AllocationStatus`, a `ProcessStatus`, a raw
105/// `Value`) and delegated the `{"status": …}` wrap uniformly, so
106/// [`merge_status`] owns that wrap; every primary-resource writer
107/// composed a task-specific body (a `spec:` slot for a spec patch, a
108/// `metadata:` slot for a finalizer / annotation edit, a `data:` slot
109/// for a ConfigMap edit) with no shared top-level shape, so this
110/// primitive dispatches the caller's body verbatim rather than
111/// speculating a wrap. A future normalization that WOULD apply to every
112/// primary-resource writer (a hardcoded field-manager pass-through for
113/// primary-resource merge writes, a strategic-merge escape hatch, a
114/// dry-run gate, a `resourceVersion` precondition slot) lands at THIS
115/// ONE function and every downstream consumer inherits the upgrade
116/// mechanically.
117///
118/// Pre-lift the 3-link chain
119/// `api.patch(name, &PatchParams::default(), &Patch::Merge(&body))` was
120/// hand-authored at SIX consumer sites past the ★★ PRIME-DIRECTIVE ≥ 2
121/// duplication threshold, spanning TWO workspace crates:
122/// * `tatara-reconciler::patch::patch_process_table_spec` — the
123///   `{"spec": ...}` merge that stamps `next_sequence` bumps on the
124///   ProcessTable singleton.
125/// * `tatara-reconciler::patch::apply_finalizer_transform` — the
126///   `{"metadata": {"finalizers": [...]}}` merge that owns finalizer
127///   ensure / remove on the Process (shared by both public wrappers).
128/// * `tatara-reconciler::signals::ingest` — the
129///   `{"metadata": {"annotations": {SIGNAL: null}}}` merge that strips
130///   the tatara-pleme-io/signal annotation off the Process after
131///   ingestion.
132/// * `tatara-reconciler::signals::consume_effect` (`SignalEffect::Suspend`
133///   arm) — the `{"spec": {"suspended": true}}` merge that stamps
134///   SIGSTOP-persistent suspend state on the Process.
135/// * `tatara-reconciler::signals::consume_effect` (`SignalEffect::Resume`
136///   arm) — the `{"spec": {"suspended": false}}` merge that lifts
137///   suspend state on SIGCONT.
138/// * `tatara-closed-loop-probe::main::write_receipt_configmap` (409
139///   already-exists retry path) — the `{"data": <receipt payload>}`
140///   merge that updates the receipt ConfigMap in-place when the create
141///   arm loses the race with a prior probe emission.
142///
143/// Post-lift each callsite reads `patch::merge(&api, name, &body)` and
144/// the 3-link chain lives at ONE substrate owner. The pin block below
145/// binds the primitive at fail-before-pass-after granularity so a
146/// regression that drops `Patch::Merge` for `Patch::Strategic`, drifts
147/// the `PatchParams::default()` slot, or reorders the 3-arg positional
148/// slots surfaces here rather than as silent primary-resource writer
149/// skew across the two consumer crates.
150///
151/// Theory anchor: THEORY.md §VI.1 (generation over composition — the
152/// 3-link primary-resource merge chain recurred at 6 hand-authored
153/// sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication trigger, and is
154/// lifted onto the ONE workspace-wide substrate owner here). THEORY.md
155/// §II.1 invariant 5 (composition preserves proofs — the pin block
156/// binds the `Patch::Merge` posture + the default `PatchParams` slot +
157/// the pass-through body composition + the byte-identical parity with
158/// the pre-lift 3-link chain, so a regression that drifted any surface
159/// surfaces here rather than as silent operator-facing skew across the
160/// six primary-resource writer sites).
161pub async fn merge<K, B>(api: &Api<K>, name: &str, body: &B) -> Result<K, kube::Error>
162where
163    K: Resource + DeserializeOwned + Clone + Debug,
164    K::DynamicType: Default,
165    B: Serialize + Debug + ?Sized,
166{
167    api.patch(name, &PatchParams::default(), &Patch::Merge(body))
168        .await
169}
170
171/// Server-side-apply [`PatchParams`] with `field_manager` bound to the
172/// caller-supplied slot and `force = true` — the ONE substrate
173/// primitive owning the `PatchParams::apply(<mgr>).force()` incantation
174/// every workspace SSA writer restated by hand pre-lift.
175///
176/// SSA-side sibling to [`merge_status`] on the (wire-posture × axis)
177/// pair: [`merge_status`] owns the merge-patch axis
178/// (`Patch::Merge + PatchParams::default()` over `/status`); this
179/// primitive owns the server-side-apply axis
180/// (`Patch::Apply + PatchParams::apply(<mgr>).force()` over the primary
181/// resource). Together they own the two wire-side write-posture
182/// primitives the workspace's controllers stamp.
183///
184/// Pre-lift the 2-link chain was hand-authored at THREE consumer sites
185/// past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold, spanning THREE
186/// crates:
187/// * `tatara-pool-reconciler::controller_allocation` (bind arm +
188///   release arm) — `PatchParams::apply(&ctx.config.field_manager)
189///   .force()` on the Process patch that stamps requestor / allocation
190///   binding annotations, and on the return-trigger annotation patch.
191/// * `tatara-export-worker::main::write_receipt` — `PatchParams::apply
192///   ("tatara-export-worker").force()` on the receipt ConfigMap apply.
193///
194/// And a fourth site owns the reconciler-crate-local
195/// [`FIELD_MANAGER`]-bound wrapper
196/// (`tatara_reconciler::ssapply::apply_patch_params`), which post-lift
197/// delegates to THIS substrate primitive rather than re-stating the
198/// chain: the SSA-side wire posture now has ONE workspace-wide owner.
199///
200/// The `field_manager` slot is caller-supplied because the SSA writers
201/// this primitive serves span three different field-manager
202/// disciplines:
203/// * `tatara-reconciler` — a `pub const FIELD_MANAGER: &str =
204///   "tatara-reconciler"` bound at the reconciler-crate wrapper.
205/// * `tatara-pool-reconciler` — a per-instance `ctx.config.field_manager`
206///   String, so a per-shard or per-cluster deployment can distinguish
207///   its allocator's SSA writes from a sibling deployment's.
208/// * `tatara-export-worker` — a `"tatara-export-worker"` literal, so
209///   the reconciler / operator distinguishes worker-emitted receipt
210///   ConfigMaps from reconciler-emitted resources at field-manager
211///   ownership queries.
212///
213/// The `force = true` semantics matches the SSA `force` directive every
214/// pre-lift chain applied — every consumer of this primitive is the
215/// authoritative owner of the field pathways it stamps
216/// (rendered-resource annotations, `RELEASED_FROM` marker,
217/// `ProcessTable.status.claims`, allocation-bind annotations, receipt
218/// ConfigMap data) and reclaims conflicting slots from prior
219/// field-manager owners on every apply.
220///
221/// A `#[must_use]` return keeps a caller from building a `PatchParams`
222/// via this primitive and then dropping it un-passed to `Api::patch`;
223/// the primitive exists to be consumed at a wire-side write, not to
224/// probe field-manager state.
225///
226/// Theory anchor: THEORY.md §VI.1 (generation over composition — the
227/// `.apply(<mgr>).force()` chain recurred at 3 hand-authored sites
228/// past the ★★ PRIME-DIRECTIVE ≥ 2 duplication trigger, spanning three
229/// workspace crates, and is lifted to ONE workspace-wide substrate
230/// owner here). THEORY.md §II.1 invariant 5 (composition preserves
231/// proofs — the pin block below binds the primitive at
232/// fail-before-pass-after granularity, so a regression that drops
233/// `.force()`, drifts the field-manager pass-through, or widens the
234/// posture surfaces at THESE pins rather than as silent SSA writer
235/// skew across the three consumer crates).
236#[must_use]
237pub fn apply_patch_params(field_manager: &str) -> PatchParams {
238    PatchParams::apply(field_manager).force()
239}
240
241/// Server-side-apply the caller-composed `body` against the PRIMARY resource
242/// endpoint of any kube [`Resource`] under `field_manager` with `force = true`.
243///
244/// SSA-side sibling to [`merge`] on the (wire-endpoint × wrap-posture) pair:
245/// [`merge`] owns the primary-resource `Patch::Merge + PatchParams::default()`
246/// axis; this primitive owns the primary-resource
247/// `Patch::Apply + PatchParams::apply(<mgr>).force()` axis and composes the
248/// two-link `apply_patch_params + api.patch(&Patch::Apply(...))` chain every
249/// workspace SSA writer hand-authored pre-lift at each ownership-taking
250/// apply site.
251///
252/// Pre-lift the 3-link chain
253/// `let pp = apply_patch_params(<mgr>);
254///  api.patch(name, &pp, &Patch::Apply(&body)).await`
255/// was hand-authored at THREE workspace-wide consumer sites past the ★★
256/// PRIME-DIRECTIVE ≥ 2 duplication threshold, spanning TWO active crates:
257/// * `tatara-reconciler::ssapply::apply_owned` — the DynamicObject SSA
258///   writer for every rendered flux/aplicacao resource; the manager
259///   is [`tatara_reconciler::ssapply::FIELD_MANAGER`].
260/// * `tatara-reconciler::phase_machine::transition_to_releasing` — the
261///   `RELEASED_FROM` annotation stamp on Attested/Failed → Releasing;
262///   same manager as above.
263/// * `tatara-export-worker::main::write_receipt` — the receipt ConfigMap
264///   SSA apply; the manager is the `"tatara-export-worker"` literal.
265///
266/// All three sites walked the SAME two-link chain — build a `PatchParams`
267/// via [`apply_patch_params`], then dispatch through
268/// `api.patch(name, &pp, &Patch::Apply(&body))`. Post-lift each callsite
269/// reads `tatara_process::patch::apply(&api, name, <mgr>, &body).await`
270/// and the params-build + `Patch::Apply` wire dispatch lives at ONE
271/// substrate owner.
272///
273/// The `field_manager` slot is caller-supplied because the three SSA
274/// writers this primitive serves span two field-manager disciplines:
275/// tatara-reconciler feeds its `FIELD_MANAGER` const (via the
276/// crate-local `ssapply::apply_patch_params()` wrapper's callers, which
277/// after this lift call THIS primitive with the const directly),
278/// tatara-export-worker feeds the `"tatara-export-worker"` literal.
279///
280/// A future normalization of the SSA-side wire posture (an injectable
281/// `dry_run` mode, a `field_validation` default, a per-fleet retry
282/// policy, a `resourceVersion` precondition slot, a `tracing`-annotated
283/// span carrying the apply's manager + body-summary for post-hoc audit)
284/// lands at THIS ONE substrate primitive (or at [`apply_patch_params`]
285/// on the params sub-axis) and every downstream SSA writer inherits
286/// the upgrade mechanically. No per-site edit at any of the three
287/// listed callers or at future consumers (a new SSA writer for a
288/// non-DynamicObject typed resource, a fourth crate stamping receipts,
289/// a per-Kind apply sink).
290///
291/// Return-form axis: `Result<K, kube::Error>` matches `Api::patch`
292/// verbatim. Consumers today either drop the returned `K`
293/// (`.await.map_err(...)?` at ssapply + phase_machine) or discard it
294/// through `.await.map(|_| ()).with_context(...)?` at export-worker;
295/// keeping the return in the signature lets a future writer that needs
296/// the reconciled `resourceVersion` / `generation` from the same wire
297/// round-trip read it without a re-fetch.
298///
299/// Theory anchor: THEORY.md §VI.1 (generation over composition — the
300/// 2-link `apply_patch_params + api.patch(&Patch::Apply(...))` chain
301/// recurred at 3 hand-authored sites past the ★★ PRIME-DIRECTIVE ≥ 2
302/// duplication trigger, spanning two workspace crates, and is lifted
303/// onto ONE substrate owner here). THEORY.md §II.1 invariant 5
304/// (composition preserves proofs — the pin block below binds the
305/// `Patch::Apply` posture + the [`apply_patch_params`] pass-through +
306/// the byte-identical parity with the pre-lift chain, so a regression
307/// that drifts any surface surfaces here rather than as silent SSA
308/// writer skew across the three primary-resource apply sites).
309pub async fn apply<K, B>(
310    api: &Api<K>,
311    name: &str,
312    field_manager: &str,
313    body: &B,
314) -> Result<K, kube::Error>
315where
316    K: Resource + DeserializeOwned + Clone + Debug,
317    B: Serialize + Debug + ?Sized,
318{
319    // NOTE: `K::DynamicType: Default` is deliberately NOT required here
320    // (unlike [`merge`] / [`merge_status`]) so [`Api<DynamicObject>`]
321    // consumers — whose `DynamicType = ApiResource` is not `Default` —
322    // ride the same primitive as concrete `Api<ConfigMap>` /
323    // `Api<Process>` consumers. `Api::patch` itself needs only
324    // `K: Clone + DeserializeOwned + Debug` on its own impl block; the
325    // `Default` bound on the sibling primitives is a legacy of their
326    // pre-lift call sites, none of which exercised DynamicObject.
327    let pp = apply_patch_params(field_manager);
328    api.patch(name, &pp, &Patch::Apply(body)).await
329}
330
331/// Compose the merge-patch wire body `{"spec": {"suspended": <bool>}}` — the
332/// SIGSTOP/SIGCONT-driven suspend/resume shape both
333/// `SignalEffect::Suspend` and `SignalEffect::Resume` arms of
334/// `tatara-reconciler::signals::consume_effect` stamp on the Process spec.
335///
336/// Both arms compose through this ONE substrate owner and hand the produced
337/// body straight to [`merge`]; pre-lift each arm restated `json!({ "spec":
338/// { "suspended": <bool> } })` verbatim at its callsite (both are named in
339/// the `merge` docstring's six-consumer inventory above). Two hand-authored
340/// restatements past the ★★ PRIME-DIRECTIVE ≥ 2 duplication trigger; post-
341/// lift a future addition to the suspend/resume wire body (a `by:` slot
342/// naming the signal source, a `suspendedAt:` transition timestamp, a
343/// symmetry gate that refuses conflicting suspend + resume overlays, a
344/// version-tagged wrap for a `spec.suspend.v2` migration) lands at THIS
345/// function and both arms inherit the upgrade mechanically.
346///
347/// The `bool` argument matches the pre-lift call sites' spelling exactly
348/// (`true` at the Suspend arm, `false` at the Resume arm) — the primitive
349/// does not force one polarity, because the merge-patch body itself is
350/// symmetric between the two arms and the shape stays load-bearing at
351/// both polarities.
352///
353/// Sibling to [`merge_status_body`] on the (wire-endpoint × wrap-posture)
354/// pair: [`merge_status_body`] owns the `/status` subresource wrap;
355/// this primitive owns one specific `{"spec": …}` primary-resource wrap
356/// (the suspend/resume one) — a body composer, not a wire-dispatcher, so
357/// consumers still hand the produced body to [`merge`] for the round-
358/// trip.
359///
360/// Theory anchor: THEORY.md §VI.1 (generation over composition — the
361/// two-arm `json!({ "spec": { "suspended": <bool> } })` restatement is
362/// lifted onto ONE substrate composer). THEORY.md §II.1 invariant 5
363/// (composition preserves proofs — the pin block below binds the shape
364/// at fail-before-pass-after granularity so a regression that drifts the
365/// top-level `spec` slot, the inner `suspended` slot, or the JSON bool
366/// value type at either polarity surfaces here rather than as silent
367/// signal-arm skew at the two suspend/resume callsites).
368#[must_use]
369pub fn spec_suspended_body(suspended: bool) -> serde_json::Value {
370    json!({ "spec": { "suspended": suspended } })
371}
372
373/// Compose the merge-patch wire body
374/// `{"metadata": {"annotations": {<key>: <value>}}}` — the ONE substrate
375/// owner of the single-annotation stamp / strip merge-body shape every
376/// workspace controller reaches for when it needs to publish exactly ONE
377/// operator-visible annotation on the primary resource (or strip one by
378/// stamping `Value::Null`) through the merge-patch semantics of either
379/// [`merge`] or [`apply`].
380///
381/// Pre-lift the wire-shape recurred at THREE hand-authored consumer
382/// sites across TWO active workspace crates past the ★★ PRIME-DIRECTIVE
383/// ≥ 2 duplication threshold:
384///
385/// - `tatara-reconciler::signals::ingest` — strips the
386///   `tatara.pleme.io/signal` annotation off the Process after
387///   ingestion by stamping `serde_json::Value::Null` (JSON merge patch
388///   interprets `null` as "remove key"). Dispatched through
389///   [`merge`] on the primary-resource merge-patch axis.
390/// - `tatara-reconciler::phase_machine::transition_to_releasing` —
391///   stamps the caller-observed `tatara.pleme.io/released-from`
392///   annotation with the current phase string on Attested/Failed →
393///   Releasing. Dispatched through [`apply`] on the primary-resource
394///   SSA axis (SSA `Patch::Apply` accepts the same
395///   `{"metadata": {"annotations": …}}` body shape as `Patch::Merge`
396///   — the top-level slot naming is what this composer owns).
397/// - `tatara-pool-reconciler::controller_allocation` (Release arm) —
398///   stamps the `tatara.pleme.io/return-trigger` annotation with the
399///   literal `"true"` on the member Process to nudge the Pool
400///   reconciler into taking the return path. Dispatched through the
401///   raw `Api::patch` call inside the release arm (also with
402///   [`apply_patch_params`]-composed PatchParams; the wire shape is
403///   the same `{"metadata": {"annotations": {<one key>: <one value>}}}`
404///   this composer names).
405///
406/// Post-lift each site reads `tatara_process::patch::annotation_body(
407/// <key>, <value>)` and the merge-body wire-shape composition lives at
408/// ONE substrate owner. A future normalization of the single-annotation
409/// merge-body posture (a canonicalization pass over the key spelling —
410/// a case-fold or a namespace-prefix normalization for a future annotation
411/// naming discipline; a stricter serde-failure return in place of the
412/// silent `Value::Null` fallback; a `by:` sibling slot naming the
413/// stamping controller for post-hoc audit; a version-tagged wrap for a
414/// future `metadata.v2.annotations` migration) lands at THIS ONE function
415/// and every downstream single-annotation writer inherits the upgrade
416/// mechanically. Directly benefits the P3 kenshi-runner library lift
417/// (any Job-based observer that stamps a per-suite annotation on its
418/// owning Process rides through the same composer as the strip / stamp
419/// / return-trigger family) and the P5 shigoto Dag refactor (every
420/// phase-machine RecordingJob that stamps an annotation on a transition
421/// rides through the same composer).
422///
423/// ### Value axis — `impl Serialize` accepts every pre-lift shape
424///
425/// The `value` slot is `impl Serialize` matching the discipline of
426/// [`phase_status_with`] on the extra-key axis: accepts owned or borrowed
427/// values of any serde-serialisable type without widening the signature.
428/// All three pre-lift consumer sites pass distinct value shapes and this
429/// composer serves each verbatim through `serde_json::to_value`:
430///
431/// - `serde_json::Value::Null` (signals::ingest strip) — the primitive
432///   [`serde_json::to_value`] round-trips a `Value::Null` back to
433///   `Value::Null`, which JSON merge patch interprets as "remove key".
434/// - `String` (phase_machine::transition_to_releasing) — the primitive
435///   [`serde_json::to_value`] serializes a `String` to a JSON string
436///   verbatim.
437/// - `&'static str` (controller_allocation Release arm) — the primitive
438///   [`serde_json::to_value`] serializes a `&str` to a JSON string
439///   verbatim, matching the pre-lift `"true"` literal.
440///
441/// A serialisation failure resolves to `Value::Null`, matching the
442/// existing [`phase_status_with`] primitive's posture. In practice
443/// serialisation of the shapes this composer accepts (a
444/// `serde_json::Value`, a `String`, a `&str`) never fails; the fallback
445/// is a defensive guard against a future caller passing a `T: Serialize`
446/// whose `Serialize` impl signals a runtime error.
447///
448/// ### Key axis — `&str` matches every pre-lift call form
449///
450/// The `key` slot is `&str` matching the pre-lift call forms exactly:
451/// [`crate::annotations::SIGNAL`] via `SIGNAL_ANNOTATION: &str` at
452/// signals.rs, [`crate::annotations::RELEASED_FROM`] via a `pub const:
453/// &str` at phase_machine.rs, and a `"tatara.pleme.io/return-trigger"`
454/// literal at controller_allocation.rs. `&str` accepts both the
455/// pre-existing `pub const: &str` constants in [`crate::annotations`]
456/// and inline `&'static str` literals at the same signature.
457///
458/// A future caller composing a `String` key at runtime (a per-fleet
459/// prefix, a runtime-computed annotation name) coerces via `&*key`
460/// or `key.as_str()` at the call site — the composer stays borrowed
461/// so the common const-fed path pays no allocation.
462///
463/// ### `must_use` on the return
464///
465/// The primitive exists to be handed to a wire-side write ([`merge`],
466/// [`apply`], or a raw `Api::patch` call at the pool-reconciler's
467/// release arm), not to probe the merge-body shape. `#[must_use]`
468/// keeps a caller from building the body and dropping it un-passed to
469/// a wire dispatcher.
470///
471/// Theory anchor: THEORY.md §VI.1 (generation over composition — the
472/// 3-link `json!({"metadata": {"annotations": {<key>: <value>}}})` merge-
473/// body composition recurred at 3 hand-authored sites past the ★★
474/// PRIME-DIRECTIVE ≥ 2 duplication trigger, spanning two active
475/// workspace crates, and is lifted onto ONE substrate owner here).
476/// THEORY.md §II.1 invariant 5 (composition preserves proofs — the pin
477/// block below binds the composer at fail-before-pass-after granularity,
478/// so a regression that drifts the top-level `metadata` slot, the nested
479/// `annotations` slot, the caller-passed key spelling, or the value-slot
480/// pass-through discipline surfaces HERE rather than as silent
481/// operator-facing annotation-writer skew across the three consumer
482/// sites).
483#[must_use]
484pub fn annotation_body(key: &str, value: impl Serialize) -> serde_json::Value {
485    let v = serde_json::to_value(value).unwrap_or(serde_json::Value::Null);
486    json!({
487        "metadata": {
488            "annotations": {
489                key: v,
490            }
491        }
492    })
493}
494
495#[cfg(test)]
496mod tests {
497    use super::*;
498    use serde::Serialize;
499    use serde_json::json;
500
501    // ─── merge_status_body substrate pins ───────────────────────────
502    //
503    // The pre-lift `json!({"status": <typed>})` wrap recurred at 7
504    // hand-authored sites across `tatara-pool-reconciler` (both
505    // controllers) + `tatara-reconciler::patch::patch_process_status`
506    // pre-lift. These pins bind the wire-body shape at
507    // fail-before-pass-after granularity so a regression that drifts
508    // the top-level slot key, reshapes the wrap posture, or leaks a
509    // sibling slot surfaces here rather than as silent status-write
510    // drift at every downstream controller.
511
512    #[test]
513    fn merge_status_body_wraps_typed_status_under_top_level_status_slot() {
514        #[derive(Serialize)]
515        struct S {
516            phase: &'static str,
517            reason: &'static str,
518        }
519        let body = merge_status_body(&S {
520            phase: "Bound",
521            reason: "member allocated",
522        });
523        assert_eq!(
524            body,
525            json!({ "status": { "phase": "Bound", "reason": "member allocated" } }),
526        );
527    }
528
529    #[test]
530    fn merge_status_body_top_level_key_is_exactly_status_lowercase() {
531        // Any drift on the top-level slot name (case-fold to `Status`,
532        // a substrate-side rename to `status_patch`, a version-tagged
533        // wrap like `v1alpha1_status`) breaks every status writer on
534        // the wire. This pin binds the exact spelling downstream K8s
535        // API + K8s-openapi generated types expect.
536        let body = merge_status_body(&json!({"phase": "Running"}));
537        let obj = body.as_object().expect("top-level must be a JSON object");
538        assert_eq!(obj.len(), 1, "wrap adds exactly ONE top-level slot");
539        assert!(
540            obj.contains_key("status"),
541            "top-level slot must be exactly `status` (lowercase)"
542        );
543    }
544
545    #[test]
546    fn merge_status_body_accepts_pre_serialized_json_value_verbatim() {
547        // Callers that already have a `serde_json::Value` (e.g. the
548        // existing `tatara-reconciler::patch::patch_process_status`
549        // callers that hand-build a `Value` via one of the
550        // `phase_status_*` builders) pass it directly to the primitive
551        // without re-serialization. This pin binds that pass-through
552        // shape: the wrap layer never re-encodes an already-JSON slot.
553        let pre = json!({"phase": "Attested", "phaseSince": "2026-05-01T00:00:00Z"});
554        let body = merge_status_body(&pre);
555        assert_eq!(body, json!({"status": pre}));
556    }
557
558    #[test]
559    fn merge_status_body_wraps_scalar_status_without_object_promotion() {
560        // The primitive is not "wrap into an object with a phase
561        // slot" — it is exactly "wrap into `{"status": <serialized>}`".
562        // A scalar status (unusual in practice, but permitted by the
563        // Serialize bound) rides through as the top-level `status`
564        // value verbatim.
565        let body = merge_status_body(&"Attested");
566        assert_eq!(body, json!({"status": "Attested"}));
567    }
568
569    #[test]
570    fn merge_status_body_preserves_struct_update_composition_bytewise() {
571        // The pool-reconciler `AllocationStatus { bound_pool: Some(p),
572        // ..AllocationStatus::transition(...) }` struct-update shape
573        // composes a typed value that serialize into a stable JSON
574        // shape. This pin binds a smaller-scale peer: a struct-update
575        // over a base composer produces the same JSON as the fully
576        // spelled-out struct literal.
577        #[derive(Serialize)]
578        struct Base {
579            phase: &'static str,
580            phase_since: &'static str,
581            extra: Option<&'static str>,
582        }
583        fn base() -> Base {
584            Base {
585                phase: "Queued",
586                phase_since: "2026-05-01T00:00:00Z",
587                extra: None,
588            }
589        }
590        let struct_update = Base {
591            extra: Some("pool matched"),
592            ..base()
593        };
594        let spelled_out = Base {
595            phase: "Queued",
596            phase_since: "2026-05-01T00:00:00Z",
597            extra: Some("pool matched"),
598        };
599        assert_eq!(
600            merge_status_body(&struct_update),
601            merge_status_body(&spelled_out),
602            "struct-update composition serializes byte-identically to the fully-spelled struct literal",
603        );
604    }
605
606    // ─── merge_status wire-side round-trip pin ──────────────────────
607    //
608    // Bind that the async entry composes the same wire body the pure
609    // helper does (i.e. `merge_status` delegates to
610    // `merge_status_body` verbatim rather than restating the wrap).
611    // A regression that hand-rolled the wrap inside `merge_status`
612    // (thereby drifting from `merge_status_body`'s pinned shape) would
613    // surface here.
614    #[test]
615    fn merge_status_delegates_wire_body_construction_to_merge_status_body() {
616        // The invariant this binds is a source-level one: whichever
617        // call path a caller takes (direct body-construction, or the
618        // async entry composing internally), the wire body is the same
619        // shape. We witness it by having both call sites hit the same
620        // helper. The pure helper's pins above cover the shape; this
621        // pin binds the wire-side entry does not fork.
622        let body_via_helper = merge_status_body(&json!({"phase": "Running"}));
623        // `merge_status` is `async` and needs an `Api<K>` we cannot
624        // construct here without a client — but its body composition
625        // step calls exactly `merge_status_body(status)`, so the pin
626        // above already covers the shape. This test exists to name the
627        // delegation invariant so a future refactor that inlined the
628        // wrap would need to move THIS pin's docstring first.
629        assert_eq!(body_via_helper["status"]["phase"], "Running");
630    }
631
632    // ─── apply_patch_params substrate pins ──────────────────────────
633    //
634    // The 2-link `PatchParams::apply(<mgr>).force()` chain now rides
635    // through the ONE substrate primitive [`apply_patch_params`]
636    // across THREE consumer crates: `tatara-reconciler::ssapply`
637    // (field-manager-const-bound wrapper delegating to this one),
638    // `tatara-pool-reconciler::controller_allocation` (bind + release
639    // arms, feeding a per-instance `ctx.config.field_manager` String
640    // through the pass-through slot), `tatara-export-worker::main::
641    // write_receipt` (feeding a `"tatara-export-worker"` literal
642    // through the same slot). These pins bind the primitive at
643    // fail-before-pass-after granularity so a regression that drops
644    // `.force()`, drifts the field-manager pass-through, reintroduces
645    // a hand-authored literal at any consumer, or widens the posture
646    // (auto-`dry_run`, non-`None` `field_validation`) surfaces HERE
647    // rather than as silent SSA writer skew across three workspace
648    // crates.
649
650    #[test]
651    fn apply_patch_params_binds_field_manager_pass_through_slot_verbatim() {
652        // The pass-through slot is byte-identical to the caller's
653        // `&str`: no re-encoding, no case-fold, no substitution. A
654        // regression that trimmed / normalized the manager string
655        // silently would surface here — every consumer relies on the
656        // exact spelling landing in the SSA wire request so downstream
657        // field-manager ownership queries key on the exact identity
658        // each callsite stamps.
659        let pp = apply_patch_params("tatara-reconciler");
660        assert_eq!(pp.field_manager.as_deref(), Some("tatara-reconciler"));
661
662        let pp = apply_patch_params("tatara-export-worker");
663        assert_eq!(pp.field_manager.as_deref(), Some("tatara-export-worker"));
664
665        let pp = apply_patch_params("per-shard-manager-42");
666        assert_eq!(pp.field_manager.as_deref(), Some("per-shard-manager-42"));
667    }
668
669    #[test]
670    fn apply_patch_params_stamps_force_true() {
671        // `force = true` matches the SSA `force` directive every pre-
672        // lift chain applied at every SSA writer site across the three
673        // consumer crates — every consumer is the authoritative owner
674        // of the field pathways it stamps and reclaims conflicting
675        // slots on every apply. A regression that dropped `.force()`
676        // from the primitive would silently 409-conflict at every SSA
677        // write on any field already owned by a prior field manager.
678        let pp = apply_patch_params("tatara-reconciler");
679        assert!(pp.force);
680    }
681
682    #[test]
683    fn apply_patch_params_defaults_dry_run_and_field_validation_off() {
684        // The primitive stamps ONLY the `field_manager` + `force` slots
685        // every pre-lift chain stamped — `dry_run` stays `false` and
686        // `field_validation` stays `None`. A regression that widened
687        // the primitive's slot set (auto-enabled `dry_run` during a
688        // debug pass, added a default `field_validation` mode) would
689        // silently no-op every SSA write (dry_run) or reject apply
690        // bodies previous consumers accepted (field_validation).
691        let pp = apply_patch_params("tatara-reconciler");
692        assert!(!pp.dry_run);
693        assert!(pp.field_validation.is_none());
694    }
695
696    #[test]
697    fn apply_patch_params_matches_pre_lift_hand_authored_chain_bytewise() {
698        // Byte-shape parity with the pre-lift 2-link chain at every
699        // observable slot (`field_manager`, `force`, `dry_run`,
700        // `field_validation`) at each of the three consumer crates'
701        // hand-authored spellings. A regression that reordered the
702        // chain (e.g. `apply(...).dry_run().force()` swap) or drifted
703        // any slot's wire representation lands HERE.
704        for mgr in [
705            "tatara-reconciler",
706            "tatara-export-worker",
707            "per-shard-manager-42",
708        ] {
709            let pre_lift = PatchParams::apply(mgr).force();
710            let lifted = apply_patch_params(mgr);
711            assert_eq!(lifted.field_manager, pre_lift.field_manager);
712            assert_eq!(lifted.force, pre_lift.force);
713            assert_eq!(lifted.dry_run, pre_lift.dry_run);
714            assert_eq!(
715                lifted.field_validation.is_none(),
716                pre_lift.field_validation.is_none()
717            );
718        }
719    }
720
721    // ─── merge (primary-resource) substrate pins ────────────────────
722    //
723    // The 3-link `api.patch(name, &PatchParams::default(),
724    // &Patch::Merge(&body))` chain now rides through the ONE substrate
725    // primitive [`merge`] across TWO consumer crates:
726    // `tatara-reconciler::patch::{patch_process_table_spec,
727    // apply_finalizer_transform}` + `tatara-reconciler::signals::
728    // {ingest, consume_effect (Suspend + Resume arms)}` and
729    // `tatara-closed-loop-probe::main::write_receipt_configmap`. These
730    // pins bind the primitive at fail-before-pass-after granularity so
731    // a regression that switches `Patch::Merge` for `Patch::Strategic`,
732    // drifts `PatchParams::default()` to a non-default posture (a
733    // hardcoded field manager, an auto-`dry_run`, a non-`None`
734    // `field_validation` mode), reorders the 3-arg positional slots,
735    // or hijacks the pass-through body (a hidden top-level wrap, an
736    // accidental re-encode through `serde_json::to_value` and back)
737    // surfaces HERE rather than as silent primary-resource writer skew
738    // across the six pre-lift callsites.
739    //
740    // These are source-level pins on the pure helpers the async entry
741    // composes: the wire-side round-trip needs a live `Api<K>` we
742    // cannot construct without a kube client, but the substrate's
743    // async entry is a single-expression delegation to
744    // `api.patch(name, &PatchParams::default(), &Patch::Merge(body))`,
745    // so binding each ingredient (default patch-params posture, merge-
746    // strategy selection, verbatim body pass-through) at the pure
747    // level pins every observable slot of the wire request the primitive
748    // will issue.
749
750    #[test]
751    fn merge_uses_default_patch_params_posture_no_field_manager_no_dry_run_no_force() {
752        // The primary-resource merge primitive stamps the DEFAULT
753        // `PatchParams` posture — no field_manager (merge writes are
754        // not SSA and do not participate in the field-manager
755        // ownership model), no dry_run, no force, no field_validation.
756        // A regression that swapped in a partially-populated
757        // `PatchParams` (a stray `apply(...)`, a debug-mode `dry_run`,
758        // a `field_validation` mode) would silently reshape every
759        // primary-resource merge into an SSA-adjacent or dry-run write.
760        let pp = PatchParams::default();
761        assert!(pp.field_manager.is_none(), "default has no field_manager");
762        assert!(!pp.dry_run, "default has dry_run false");
763        assert!(!pp.force, "default has force false");
764        assert!(
765            pp.field_validation.is_none(),
766            "default has no field_validation"
767        );
768    }
769
770    #[test]
771    fn merge_selects_patch_merge_strategy_not_apply_or_strategic() {
772        // The primitive dispatches through `Patch::Merge(&body)` — the
773        // JSON merge patch posture (RFC 7396) every pre-lift consumer
774        // used. A regression that selected `Patch::Apply` would inject
775        // an SSA wire request against the primary-resource endpoint
776        // (which either 415s without an `apiVersion`/`kind` slot or
777        // takes ownership away from the API server's merge
778        // reconciliation model); a regression that selected
779        // `Patch::Strategic` would reshape merge semantics for arrays
780        // of tagged sub-objects (finalizers, annotations, labels) into
781        // strategic-merge behavior that silently deduplicates entries
782        // by strategic-merge-key rather than treating the slot as a
783        // JSON scalar to overwrite.
784        let body = json!({"spec": {"suspended": true}});
785        let patch: Patch<&serde_json::Value> = Patch::Merge(&body);
786        assert!(
787            matches!(patch, Patch::Merge(_)),
788            "merge primitive dispatches through Patch::Merge, not Apply/Strategic/Json"
789        );
790    }
791
792    #[test]
793    fn merge_dispatches_body_verbatim_no_wrap_or_re_encode() {
794        // Unlike [`merge_status`] which wraps its input into
795        // `{"status": …}`, the primary-resource merge primitive is
796        // verbatim: the caller composes the full top-level shape
797        // (`{"spec": …}`, `{"metadata": {"finalizers": …}}`,
798        // `{"data": …}`) and the primitive passes it through untouched.
799        // A regression that hid an implicit wrap or re-encoded the
800        // body through `serde_json::to_value` and back would surface
801        // here — every pre-lift callsite already composed the top-
802        // level shape and delegated straight to
803        // `api.patch(..., &Patch::Merge(&body))` with no intervening
804        // transform.
805        //
806        // Sweep every top-level shape the six pre-lift consumers
807        // compose so a regression on any one lands here.
808        let spec_body = json!({"spec": {"suspended": true}});
809        let meta_body = json!({
810            "metadata": {"finalizers": ["tatara.pleme.io/process-finalizer"]},
811        });
812        let strip_body = json!({
813            "metadata": {"annotations": {"tatara.pleme.io/signal": serde_json::Value::Null}},
814        });
815        let data_body = json!({"data": {"receipt.json": "{...}"}});
816        let spec_next_body = json!({"spec": {"nextSequence": 42}});
817        for body in [spec_body, meta_body, strip_body, data_body, spec_next_body] {
818            // The primitive's body-passing step is a `&Patch::Merge(body)`
819            // borrow with no intervening transform — witness that the
820            // top-level slot survives verbatim.
821            let round_trip = serde_json::to_value(&body).unwrap();
822            assert_eq!(round_trip, body, "body serializes to itself verbatim");
823            // Extract the ONE top-level slot the pre-lift caller
824            // composed; the primitive must not add a sibling slot.
825            let obj = body.as_object().expect("pre-lift bodies are JSON objects");
826            assert_eq!(
827                obj.len(),
828                1,
829                "each pre-lift consumer composed exactly ONE top-level slot"
830            );
831        }
832    }
833
834    #[test]
835    fn merge_body_composition_matches_pre_lift_signals_and_finalizer_shapes_bytewise() {
836        // Byte-shape parity against each of the six pre-lift bodies —
837        // signals::ingest strip annotation, signals::consume_effect
838        // Suspend + Resume, patch::patch_process_table_spec's
839        // `{"spec": …}` seed, patch::apply_finalizer_transform's
840        // `{"metadata": {"finalizers": …}}` seed, and
841        // closed-loop-probe::write_receipt_configmap's `{"data": …}`
842        // seed. A regression that reshaped any body composer at its
843        // callsite (case-fold slot names, added sibling debug slots)
844        // surfaces here rather than as silent behavioral drift at the
845        // wire.
846
847        // signals::ingest strip shape
848        let strip = json!({
849            "metadata": {
850                "annotations": { "tatara.pleme.io/signal": serde_json::Value::Null }
851            }
852        });
853        assert_eq!(
854            strip["metadata"]["annotations"]["tatara.pleme.io/signal"],
855            serde_json::Value::Null,
856            "strip stamps JSON null to trigger merge-patch key removal"
857        );
858
859        // signals::consume_effect Suspend shape
860        let suspend = json!({ "spec": { "suspended": true } });
861        assert_eq!(suspend["spec"]["suspended"], serde_json::Value::Bool(true));
862
863        // signals::consume_effect Resume shape
864        let resume = json!({ "spec": { "suspended": false } });
865        assert_eq!(resume["spec"]["suspended"], serde_json::Value::Bool(false));
866    }
867
868    // ─── apply (SSA primary-resource) substrate pins ───────────────
869    //
870    // The 2-link `apply_patch_params(<mgr>) + api.patch(name, &pp,
871    // &Patch::Apply(&body))` chain now rides through the ONE substrate
872    // primitive [`apply`] across TWO consumer crates:
873    // `tatara-reconciler::ssapply::apply_owned` (DynamicObject SSA
874    // writer for every rendered flux/aplicacao resource, feeding
875    // `FIELD_MANAGER` through the const wrapper),
876    // `tatara-reconciler::phase_machine::transition_to_releasing`
877    // (RELEASED_FROM annotation stamp on Attested/Failed → Releasing,
878    // same manager), and `tatara-export-worker::main::write_receipt`
879    // (receipt ConfigMap SSA apply, feeding `"tatara-export-worker"`).
880    // These pins bind the primitive at fail-before-pass-after
881    // granularity so a regression that swaps `Patch::Apply` for
882    // `Patch::Merge` (silently losing SSA ownership + reverting to
883    // merge-patch semantics), drops the [`apply_patch_params`]
884    // pass-through (silently reverting to `PatchParams::default()`
885    // and losing `.force()` + field-manager), or reorders the 3-arg
886    // positional slots surfaces HERE rather than as silent SSA
887    // writer skew across the three pre-lift callsites.
888    //
889    // These are source-level pins on the ingredients [`apply`]
890    // composes: the wire-side round-trip needs a live `Api<K>` we
891    // cannot construct without a kube client, but the substrate's
892    // async entry is a two-line body (`let pp = apply_patch_params
893    // (field_manager); api.patch(name, &pp, &Patch::Apply(body))`),
894    // so binding each ingredient (the [`apply_patch_params`]-composed
895    // PatchParams shape, the `Patch::Apply` posture selection, the
896    // verbatim body pass-through) at the pure level pins every
897    // observable slot of the SSA wire request the primitive will
898    // issue.
899
900    #[test]
901    fn apply_composes_apply_patch_params_at_the_field_manager_slot_verbatim() {
902        // The primitive's params-build step is
903        // `apply_patch_params(field_manager)` — every pre-lift caller
904        // supplied a field-manager `&str` (the reconciler's
905        // `FIELD_MANAGER` const, the export-worker's `"tatara-export-
906        // worker"` literal). A regression that hardcoded a manager
907        // inside the primitive or reshaped the slot would silently
908        // reassign field-manager ownership at every consumer's wire
909        // request. Witness the params-side ingredient by re-composing
910        // it through [`apply_patch_params`] here and checking the
911        // observable slots the SSA wire path keys on.
912        for mgr in ["tatara-reconciler", "tatara-export-worker", "per-shard-42"] {
913            let pp = apply_patch_params(mgr);
914            assert_eq!(pp.field_manager.as_deref(), Some(mgr));
915            assert!(pp.force, "SSA apply must stamp force = true");
916            assert!(!pp.dry_run, "default posture: dry_run stays false");
917            assert!(
918                pp.field_validation.is_none(),
919                "default posture: field_validation stays None",
920            );
921        }
922    }
923
924    #[test]
925    fn apply_selects_patch_apply_strategy_not_merge_or_strategic_or_json() {
926        // The primitive dispatches through `Patch::Apply(&body)` — the
927        // SSA posture (JSON server-side apply) every pre-lift consumer
928        // used to take ownership of the field pathways it stamps
929        // (rendered-resource annotations, RELEASED_FROM marker, the
930        // receipt ConfigMap). A regression that selected
931        // `Patch::Merge` would silently revert to JSON merge patch
932        // semantics — losing SSA field-manager ownership recording
933        // and dropping the `.force()` reclaim of conflicting slots;
934        // `Patch::Strategic` would reshape apply into strategic-merge
935        // over the primary resource (with the same ownership loss);
936        // `Patch::Json` would demand an RFC 6902 op list instead of
937        // the object body every consumer composes. Witness the wire
938        // posture selection by constructing the Patch and pattern-
939        // matching on the variant.
940        let body = json!({"metadata": {"annotations": {"x.io/marker": "1"}}});
941        let patch: Patch<&serde_json::Value> = Patch::Apply(&body);
942        assert!(
943            matches!(patch, Patch::Apply(_)),
944            "apply primitive dispatches through Patch::Apply, not Merge/Strategic/Json"
945        );
946    }
947
948    #[test]
949    fn apply_dispatches_body_verbatim_no_wrap_or_re_encode() {
950        // The SSA apply primitive is verbatim: the caller composes the
951        // full top-level shape (a DynamicObject serialization, a
952        // `{"metadata": {"annotations": ...}}` for the released-from
953        // stamp, a ConfigMap serialization) and the primitive passes
954        // it through untouched. A regression that hid an implicit
955        // wrap (a `{"apply": <body>}` sibling slot, an `{"kind":
956        // ..., "apiVersion": ..., "spec": <body>}` re-shape) or
957        // re-encoded the body through `serde_json::to_value` and back
958        // would surface here — every pre-lift callsite already
959        // composed the full apply body and delegated straight to
960        // `api.patch(..., &Patch::Apply(&body))` with no intervening
961        // transform.
962        //
963        // Sweep every top-level shape the three pre-lift consumers
964        // apply so a regression on any one lands here.
965        let annotation_body = json!({
966            "metadata": {"annotations": {"tatara.pleme.io/released-from": "Attested"}},
967        });
968        let configmap_body = json!({
969            "apiVersion": "v1",
970            "kind": "ConfigMap",
971            "metadata": {"name": "r", "namespace": "n"},
972            "data": {"receipt.yaml": "..."},
973        });
974        let dynamic_body = json!({
975            "apiVersion": "helm.toolkit.fluxcd.io/v2",
976            "kind": "HelmRelease",
977            "metadata": {"name": "app", "namespace": "n"},
978            "spec": {"chart": {"spec": {"chart": "app"}}},
979        });
980        for body in [annotation_body, configmap_body, dynamic_body] {
981            let round_trip = serde_json::to_value(&body).unwrap();
982            assert_eq!(round_trip, body, "body serializes to itself verbatim");
983            let obj = body.as_object().expect("pre-lift bodies are JSON objects");
984            assert!(!obj.is_empty(), "pre-lift bodies carry at least one slot");
985        }
986    }
987
988    #[test]
989    fn apply_params_match_pre_lift_hand_authored_chain_bytewise() {
990        // Byte-shape parity between the primitive's internal params
991        // composition and the pre-lift `PatchParams::apply(<mgr>)
992        // .force()` chain every consumer restated verbatim. A
993        // regression that reordered the chain (`.force().apply(...)`
994        // swap) or widened the posture inside the primitive would
995        // surface HERE rather than at the wire.
996        for mgr in ["tatara-reconciler", "tatara-export-worker"] {
997            let pre_lift = PatchParams::apply(mgr).force();
998            let lifted = apply_patch_params(mgr);
999            assert_eq!(lifted.field_manager, pre_lift.field_manager);
1000            assert_eq!(lifted.force, pre_lift.force);
1001            assert_eq!(lifted.dry_run, pre_lift.dry_run);
1002            assert_eq!(
1003                lifted.field_validation.is_none(),
1004                pre_lift.field_validation.is_none(),
1005            );
1006        }
1007    }
1008
1009    // ─── spec_suspended_body substrate pins ─────────────────────────
1010    //
1011    // The pre-lift `json!({ "spec": { "suspended": <bool> } })`
1012    // restatement recurred at TWO hand-authored sites in
1013    // `tatara-reconciler::signals::consume_effect` (Suspend arm feeding
1014    // `true`, Resume arm feeding `false`) past the ★★ PRIME-DIRECTIVE
1015    // ≥ 2 duplication threshold. These pins bind the composer at fail-
1016    // before-pass-after granularity so a regression that drifts the
1017    // top-level `spec` slot (case-fold to `Spec`, verbose rename to
1018    // `spec_patch`), the inner `suspended` slot (camelCase drift to
1019    // `Suspended`, alias rename to `paused`), the JSON bool value type
1020    // (accidental promotion to `"true"` / `"false"` strings), or the
1021    // wrap posture (a `{"metadata": {...}}` sibling slot slipping in at
1022    // the top-level) surfaces HERE rather than as silent signal-arm
1023    // skew across the two hand-authored suspend/resume callsites.
1024
1025    #[test]
1026    fn spec_suspended_body_wraps_true_under_spec_suspended_slot() {
1027        let body = spec_suspended_body(true);
1028        assert_eq!(body, json!({ "spec": { "suspended": true } }));
1029    }
1030
1031    #[test]
1032    fn spec_suspended_body_wraps_false_under_spec_suspended_slot() {
1033        let body = spec_suspended_body(false);
1034        assert_eq!(body, json!({ "spec": { "suspended": false } }));
1035    }
1036
1037    #[test]
1038    fn spec_suspended_body_top_level_slot_is_exactly_spec_lowercase() {
1039        // Any drift on the top-level slot name (case-fold to `Spec`, a
1040        // substrate-side rename to `spec_patch`, a version-tagged wrap
1041        // like `v1alpha1_spec`) breaks the merge-patch on the wire.
1042        // This pin binds the exact spelling downstream K8s API + the
1043        // Process CRD's `.spec.suspended` field path expect.
1044        for value in [true, false] {
1045            let body = spec_suspended_body(value);
1046            let obj = body.as_object().expect("top-level must be a JSON object");
1047            assert_eq!(obj.len(), 1, "wrap adds exactly ONE top-level slot");
1048            assert!(
1049                obj.contains_key("spec"),
1050                "top-level slot must be exactly `spec` (lowercase)"
1051            );
1052        }
1053    }
1054
1055    #[test]
1056    fn spec_suspended_body_inner_slot_is_exactly_suspended_lowercase() {
1057        // Any drift on the inner slot name (camelCase to `Suspended`, a
1058        // rename to `paused`, a version-tagged rename to `suspend_v2`)
1059        // breaks the merge-patch: the K8s API silently applies the wrong
1060        // field and the reconciler's suspend gate never fires.
1061        for value in [true, false] {
1062            let body = spec_suspended_body(value);
1063            let spec = body["spec"]
1064                .as_object()
1065                .expect("inner `spec` must be a JSON object");
1066            assert_eq!(
1067                spec.len(),
1068                1,
1069                "inner spec carries exactly ONE slot (`suspended`)"
1070            );
1071            assert!(
1072                spec.contains_key("suspended"),
1073                "inner slot must be exactly `suspended` (lowercase)"
1074            );
1075        }
1076    }
1077
1078    #[test]
1079    fn spec_suspended_body_inner_value_is_json_bool_not_string() {
1080        // Accidental promotion of the bool to a `"true"` / `"false"`
1081        // JSON string would silently 400 on the wire (schema validation
1082        // rejects a string on a bool field) or silently deserialize as
1083        // `Default::default()` on the field, breaking the suspend gate.
1084        assert_eq!(
1085            spec_suspended_body(true)["spec"]["suspended"],
1086            serde_json::Value::Bool(true),
1087        );
1088        assert_eq!(
1089            spec_suspended_body(false)["spec"]["suspended"],
1090            serde_json::Value::Bool(false),
1091        );
1092    }
1093
1094    #[test]
1095    fn spec_suspended_body_matches_pre_lift_hand_authored_shape_bytewise() {
1096        // Byte-shape parity with the pre-lift 2-site `json!({ "spec": {
1097        // "suspended": <bool> } })` block that both `SignalEffect::
1098        // Suspend` (true polarity) and `SignalEffect::Resume` (false
1099        // polarity) arms restated pre-lift. A regression that reshaped
1100        // either polarity would drift here rather than at the wire.
1101        for value in [true, false] {
1102            let composed = spec_suspended_body(value);
1103            let hand_authored = json!({ "spec": { "suspended": value } });
1104            assert_eq!(
1105                composed, hand_authored,
1106                "spec_suspended_body({value}) must be byte-identical to the pre-lift `json!` block",
1107            );
1108        }
1109    }
1110
1111    // ─── annotation_body substrate pins ─────────────────────────────
1112    //
1113    // The pre-lift `json!({"metadata": {"annotations": {<key>: <value>}}})`
1114    // merge-body composition recurred at THREE hand-authored consumer
1115    // sites across TWO active workspace crates past the ★★ PRIME-
1116    // DIRECTIVE ≥ 2 duplication threshold: `tatara-reconciler::signals::
1117    // ingest` (Null-value strip of the SIGNAL annotation), `tatara-
1118    // reconciler::phase_machine::transition_to_releasing` (String-value
1119    // stamp of the RELEASED_FROM annotation), and `tatara-pool-
1120    // reconciler::controller_allocation` Release arm (&str-value stamp
1121    // of the return-trigger annotation). These pins bind the composer
1122    // at fail-before-pass-after granularity so a regression that drifts
1123    // the top-level `metadata` slot (case-fold to `Metadata`, alias
1124    // rename to `meta`, version-tagged wrap like `v1_metadata`), the
1125    // nested `annotations` slot (camelCase drift to `Annotations`,
1126    // rename to `annotationMap`, a stray sibling like `labels`
1127    // leaking in), the caller-passed key spelling (silent trimming,
1128    // case-fold, per-key allow-list gate), or the value-slot pass-
1129    // through (accidental promotion of `Value::Null` to
1130    // `Value::String("null")` breaking the JSON-merge-patch strip
1131    // semantics; an over-eager `to_value` re-encoding a `Value` argument
1132    // through a `String` wrap; the fallback silently promoting a
1133    // Serialize-failure to a non-null sentinel) surfaces HERE rather
1134    // than as silent operator-facing annotation-writer skew across the
1135    // three consumer sites.
1136
1137    #[test]
1138    fn annotation_body_wraps_null_value_for_merge_patch_strip_semantics() {
1139        // Byte-shape parity witness against the `signals::ingest` pre-
1140        // lift strip block (`json!({"metadata": {"annotations":
1141        // {SIGNAL_ANNOTATION: serde_json::Value::Null}}})`) — passing
1142        // `Value::Null` at the value slot round-trips through
1143        // `serde_json::to_value` to a `Value::Null` in the composed
1144        // body, so the K8s API server's JSON-merge-patch semantics
1145        // interpret it as "remove key". A regression that promoted the
1146        // null to a `"null"` string, dropped the slot entirely, or
1147        // reshaped the null through an intermediate wrapper would
1148        // silently un-strip every signal annotation post-ingestion.
1149        let body = annotation_body("tatara.pleme.io/signal", serde_json::Value::Null);
1150        assert_eq!(
1151            body,
1152            json!({
1153                "metadata": {
1154                    "annotations": { "tatara.pleme.io/signal": serde_json::Value::Null }
1155                }
1156            }),
1157        );
1158        assert_eq!(
1159            body["metadata"]["annotations"]["tatara.pleme.io/signal"],
1160            serde_json::Value::Null,
1161            "value at the caller-passed key rides through as JSON null verbatim",
1162        );
1163    }
1164
1165    #[test]
1166    fn annotation_body_wraps_string_value_for_merge_patch_stamp_semantics() {
1167        // Byte-shape parity witness against the `phase_machine::
1168        // transition_to_releasing` pre-lift stamp block (`json!(
1169        // {"metadata": {"annotations": {RELEASED_FROM: gate}}})` where
1170        // `gate: String` is the current phase spelling) — passing an
1171        // owned `String` at the value slot round-trips through
1172        // `serde_json::to_value` to a JSON string in the composed body.
1173        // A regression that dropped the String's ownership or reshaped
1174        // it through a wrapper would silently drift the stamped value.
1175        let body = annotation_body("tatara.pleme.io/released-from", String::from("Attested"));
1176        assert_eq!(
1177            body,
1178            json!({
1179                "metadata": {
1180                    "annotations": { "tatara.pleme.io/released-from": "Attested" }
1181                }
1182            }),
1183        );
1184        assert_eq!(
1185            body["metadata"]["annotations"]["tatara.pleme.io/released-from"],
1186            serde_json::Value::String("Attested".to_string()),
1187            "String value rides through as JSON string verbatim",
1188        );
1189    }
1190
1191    #[test]
1192    fn annotation_body_wraps_str_literal_value_for_return_trigger_stamp() {
1193        // Byte-shape parity witness against the `controller_allocation`
1194        // Release-arm pre-lift stamp block (`json!({"metadata":
1195        // {"annotations": {"tatara.pleme.io/return-trigger": "true"}}})`)
1196        // — passing a `&'static str` literal at the value slot round-
1197        // trips through `serde_json::to_value` to a JSON string in the
1198        // composed body, matching the pre-lift shape byte-identically.
1199        let body = annotation_body("tatara.pleme.io/return-trigger", "true");
1200        assert_eq!(
1201            body,
1202            json!({
1203                "metadata": {
1204                    "annotations": { "tatara.pleme.io/return-trigger": "true" }
1205                }
1206            }),
1207        );
1208    }
1209
1210    #[test]
1211    fn annotation_body_top_level_slot_is_exactly_metadata_lowercase() {
1212        // Any drift on the top-level slot name (case-fold to `Metadata`,
1213        // an alias rename to `meta`, a version-tagged wrap like
1214        // `v1_metadata`) breaks the merge-patch on the wire: the K8s
1215        // API server silently applies to a sibling field the CRD does
1216        // not define, and the operator sees the annotation never
1217        // appear. This pin binds the exact spelling the K8s API server
1218        // + every generated openapi type expect.
1219        let body = annotation_body("k", "v");
1220        let obj = body.as_object().expect("top-level must be a JSON object");
1221        assert_eq!(obj.len(), 1, "wrap adds exactly ONE top-level slot");
1222        assert!(
1223            obj.contains_key("metadata"),
1224            "top-level slot must be exactly `metadata` (lowercase)"
1225        );
1226    }
1227
1228    #[test]
1229    fn annotation_body_nested_slot_is_exactly_annotations_lowercase() {
1230        // Any drift on the nested slot name (camelCase to `Annotations`,
1231        // an alias rename to `annotationMap`, a stray sibling like
1232        // `labels` leaking in) breaks the merge-patch: the K8s API
1233        // silently applies to a wrong field. This pin binds the exact
1234        // spelling downstream metadata handlers expect and guards
1235        // against a sibling-slot leak inside the metadata wrap.
1236        let body = annotation_body("k", "v");
1237        let meta = body["metadata"]
1238            .as_object()
1239            .expect("nested metadata must be a JSON object");
1240        assert_eq!(
1241            meta.len(),
1242            1,
1243            "metadata carries exactly ONE nested slot (`annotations`) — no `labels` / `finalizers` sibling leaks"
1244        );
1245        assert!(
1246            meta.contains_key("annotations"),
1247            "nested slot must be exactly `annotations` (lowercase)"
1248        );
1249    }
1250
1251    #[test]
1252    fn annotation_body_preserves_caller_key_verbatim_no_trim_or_case_fold() {
1253        // The `key` argument is stamped byte-identically as the inner
1254        // JSON slot name: no trimming of whitespace-adjacent chars, no
1255        // case-fold of any segment (a `tatara.pleme.io/RELEASED-from`
1256        // caller would land on the wire exactly that way), no per-key
1257        // allow-list gate that silently drops "unknown" annotations.
1258        // Sweep across every pre-lift caller's key spelling so a
1259        // regression that added a canonicalization pass surfaces here
1260        // rather than as a silent annotation drop at any downstream
1261        // writer.
1262        for key in [
1263            "tatara.pleme.io/signal",
1264            "tatara.pleme.io/released-from",
1265            "tatara.pleme.io/return-trigger",
1266            "custom-fleet.example.com/opaque",
1267            "SCREAMING.CASE/PRESERVED",
1268        ] {
1269            let body = annotation_body(key, "v");
1270            let annotations = body["metadata"]["annotations"]
1271                .as_object()
1272                .expect("annotations must be a JSON object");
1273            assert_eq!(
1274                annotations.len(),
1275                1,
1276                "annotations carries exactly ONE key ({key}) — no synthetic sibling leaks",
1277            );
1278            assert!(
1279                annotations.contains_key(key),
1280                "annotations key must be exactly `{key}` verbatim (no trim / case-fold / allow-list gate)",
1281            );
1282        }
1283    }
1284
1285    #[test]
1286    fn annotation_body_matches_pre_lift_hand_authored_shapes_bytewise() {
1287        // Byte-shape parity witness against all THREE pre-lift consumer
1288        // sites' hand-authored blocks — the signals::ingest strip
1289        // (Null value), the phase_machine::transition_to_releasing
1290        // stamp (String value), and the controller_allocation Release-
1291        // arm return-trigger (&str value). A regression that reshaped
1292        // ANY site's byte-shape at the composer surfaces HERE rather
1293        // than at the wire.
1294        //
1295        // Sweep three representative (key, value) tuples matching the
1296        // three pre-lift call forms.
1297        let signal_strip = annotation_body("tatara.pleme.io/signal", serde_json::Value::Null);
1298        assert_eq!(
1299            signal_strip,
1300            json!({
1301                "metadata": {
1302                    "annotations": { "tatara.pleme.io/signal": serde_json::Value::Null }
1303                }
1304            }),
1305            "signals::ingest strip byte-shape",
1306        );
1307
1308        let released_stamp =
1309            annotation_body("tatara.pleme.io/released-from", String::from("Running"));
1310        assert_eq!(
1311            released_stamp,
1312            json!({
1313                "metadata": {
1314                    "annotations": { "tatara.pleme.io/released-from": "Running" }
1315                }
1316            }),
1317            "phase_machine::transition_to_releasing stamp byte-shape",
1318        );
1319
1320        let return_trigger = annotation_body("tatara.pleme.io/return-trigger", "true");
1321        assert_eq!(
1322            return_trigger,
1323            json!({
1324                "metadata": {
1325                    "annotations": { "tatara.pleme.io/return-trigger": "true" }
1326                }
1327            }),
1328            "controller_allocation Release-arm return-trigger byte-shape",
1329        );
1330    }
1331
1332    #[test]
1333    fn annotation_body_accepts_serde_json_value_at_value_slot_without_double_wrap() {
1334        // Callers that already have a `serde_json::Value` (e.g. a
1335        // `Value::String` or `Value::Number` computed upstream via a
1336        // typed derivation) pass it directly through `impl Serialize`
1337        // without a double-wrap. A regression that re-encoded a
1338        // `Value` argument through a `String` wrap (silently producing
1339        // `Value::String("\"stamped\"")` — a JSON-encoded string of a
1340        // JSON-encoded string) would surface HERE.
1341        let pre = serde_json::Value::String("stamped".to_string());
1342        let body = annotation_body("k.io/v", pre);
1343        assert_eq!(
1344            body["metadata"]["annotations"]["k.io/v"],
1345            serde_json::Value::String("stamped".to_string()),
1346            "pre-serialized Value rides through without a double-wrap",
1347        );
1348    }
1349}