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/// Merge-patch the PRIMARY resource endpoint of any kube [`Resource`] under
332/// `field_manager` with `force = true` — the merge-strategy sibling to
333/// [`apply`] on the (Patch-strategy × PatchParams-posture) matrix.
334///
335/// Owns the two-link chain
336/// `apply_patch_params(<mgr>) + api.patch(name, &pp, &Patch::Merge(&body))`
337/// at ONE substrate owner. Closes the four-corner posture matrix the
338/// wire-side patch family stamps:
339///
340/// | Strategy | `PatchParams::default()` | `apply_patch_params(<mgr>)` |
341/// |----------|--------------------------|-----------------------------|
342/// | Merge    | [`merge`]                | **`merge_as`** (this one)   |
343/// | Apply    | (invalid — SSA requires a field manager) | [`apply`]   |
344///
345/// [`merge`] owns the anonymous-writer merge-patch corner
346/// (`PatchParams::default()`, no field-manager ownership); [`apply`] owns
347/// the SSA corner (`Patch::Apply` under a named field manager); this
348/// primitive owns the remaining corner — a merge-patch that STILL stamps
349/// a named field manager on the write, chosen when the caller wants
350/// merge-patch semantics (server merges the caller's partial body into
351/// the existing object per RFC 7396, rather than the SSA ownership
352/// reconciliation model) BUT wants the write attributed to a named
353/// controller in the field-manager ownership audit (so downstream `kubectl
354/// get -o yaml`'s `managedFields` distinguishes a
355/// `tatara-pool-reconciler`-stamped bind edit from a
356/// `tatara-reconciler`-stamped phase-transition status write).
357///
358/// Pre-lift the two-link chain was hand-authored at TWO workspace-wide
359/// consumer sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold,
360/// both inside `tatara-pool-reconciler::controller_allocation`:
361/// * Bind arm — stamps the compound `spec.lifetime` overlay + the three
362///   `metadata.annotations` requestor / allocation / requestor-kind
363///   labels onto the pool member Process on transition from Queued to
364///   Bound. Body: `{"spec": {"lifetime": …}, "metadata": {"annotations":
365///   {REQUESTOR: …, ALLOCATION: …, REQUESTOR_KIND: …}}}`. Field manager:
366///   `ctx.config.field_manager` (per-instance String).
367/// * Release arm — stamps the single `tatara.pleme.io/return-trigger`
368///   annotation onto the member Process to nudge the Pool reconciler
369///   into taking the return path. Body: [`annotation_body`]-composed
370///   single-key metadata edit. Field manager: `ctx.config.field_manager`
371///   (same String).
372///
373/// Both sites walked the SAME two-link chain — build a `PatchParams` via
374/// [`apply_patch_params`] with the pool-reconciler's per-instance
375/// `field_manager`, then dispatch through `api.patch(name, &pp,
376/// &Patch::Merge(&body))`. Post-lift each callsite reads
377/// `tatara_process::patch::merge_as(&api, name, <mgr>, &body).await`
378/// and the params-build + `Patch::Merge` wire dispatch lives at ONE
379/// substrate owner. A future normalization of the named-merge-writer
380/// posture (an injectable `dry_run` mode for a shadow-mode rollout, a
381/// `field_validation` default when the pool-reconciler flips on strict
382/// validation, an injectable retry policy for the transient-conflict
383/// class the bind arm surfaces on race with a sibling pool controller,
384/// a `resourceVersion` precondition slot when the pool controller
385/// stamps generation-fenced binds) lands at THIS ONE substrate primitive
386/// (or at [`apply_patch_params`] on the params sub-axis) and every
387/// downstream named-merge writer inherits the upgrade mechanically.
388///
389/// Directly benefits the P3 kenshi-runner library lift (any test-Job
390/// controller that stamps a named-merge overlay on its owning Process
391/// — a suite-progress annotation, a per-run bind edit — rides through
392/// the same primitive as the pool-reconciler's bind + release arms) and
393/// the P5 shigoto Dag refactor (any RecordingJob that stamps a
394/// per-instance-named merge edit on a phase transition, rather than
395/// through the [`crate::patch::apply`] SSA path or the anonymous
396/// [`merge`] path, rides through this substrate corner rather than
397/// hand-authoring the two-link chain a third time).
398///
399/// The bound relaxation `K::DynamicType: Default` is NOT required here
400/// (matching [`apply`]'s posture, differing from [`merge`] /
401/// [`merge_status`]) so a future [`Api<DynamicObject>`] consumer of the
402/// named-merge corner rides the same primitive as the current
403/// concrete-`Api<Process>` consumers. `Api::patch` itself needs only
404/// `K: Clone + DeserializeOwned + Debug` on its own impl block; the
405/// `Default` bound on the sibling merge primitives is a legacy of their
406/// pre-lift call sites, none of which exercised DynamicObject.
407///
408/// Return-form axis: `Result<K, kube::Error>` matches `Api::patch`
409/// verbatim. Both pre-lift consumers ignore the returned `K` (the bind
410/// arm captures the `Err` for a retry decision; the release arm discards
411/// through `let _ = …`); keeping the return in the signature lets a
412/// future writer that needs the reconciled `resourceVersion` /
413/// `generation` from the same wire round-trip read it without a
414/// re-fetch.
415///
416/// Theory anchor: THEORY.md §VI.1 (generation over composition — the
417/// two-link `apply_patch_params(<mgr>) + api.patch(name, &pp,
418/// &Patch::Merge(&body))` chain recurred at 2 hand-authored sites past
419/// the ★★ PRIME-DIRECTIVE ≥ 2 duplication trigger inside one workspace
420/// crate, and is lifted onto ONE substrate owner here, closing the
421/// (Patch-strategy × PatchParams-posture) matrix's remaining hand-
422/// authored corner). THEORY.md §II.1 invariant 5 (composition preserves
423/// proofs — the pin block below binds the `Patch::Merge` posture + the
424/// [`apply_patch_params`] pass-through + the byte-identical parity with
425/// the pre-lift two-link chain, so a regression that drifts any surface
426/// surfaces here rather than as silent named-merge writer skew across
427/// the two pool-reconciler callsites).
428pub async fn merge_as<K, B>(
429    api: &Api<K>,
430    name: &str,
431    field_manager: &str,
432    body: &B,
433) -> Result<K, kube::Error>
434where
435    K: Resource + DeserializeOwned + Clone + Debug,
436    B: Serialize + Debug + ?Sized,
437{
438    let pp = apply_patch_params(field_manager);
439    api.patch(name, &pp, &Patch::Merge(body)).await
440}
441
442/// Compose the merge-patch wire body `{"spec": {"suspended": <bool>}}` — the
443/// SIGSTOP/SIGCONT-driven suspend/resume shape both
444/// `SignalEffect::Suspend` and `SignalEffect::Resume` arms of
445/// `tatara-reconciler::signals::consume_effect` stamp on the Process spec.
446///
447/// Both arms compose through this ONE substrate owner and hand the produced
448/// body straight to [`merge`]; pre-lift each arm restated `json!({ "spec":
449/// { "suspended": <bool> } })` verbatim at its callsite (both are named in
450/// the `merge` docstring's six-consumer inventory above). Two hand-authored
451/// restatements past the ★★ PRIME-DIRECTIVE ≥ 2 duplication trigger; post-
452/// lift a future addition to the suspend/resume wire body (a `by:` slot
453/// naming the signal source, a `suspendedAt:` transition timestamp, a
454/// symmetry gate that refuses conflicting suspend + resume overlays, a
455/// version-tagged wrap for a `spec.suspend.v2` migration) lands at THIS
456/// function and both arms inherit the upgrade mechanically.
457///
458/// The `bool` argument matches the pre-lift call sites' spelling exactly
459/// (`true` at the Suspend arm, `false` at the Resume arm) — the primitive
460/// does not force one polarity, because the merge-patch body itself is
461/// symmetric between the two arms and the shape stays load-bearing at
462/// both polarities.
463///
464/// Sibling to [`merge_status_body`] on the (wire-endpoint × wrap-posture)
465/// pair: [`merge_status_body`] owns the `/status` subresource wrap;
466/// this primitive owns one specific `{"spec": …}` primary-resource wrap
467/// (the suspend/resume one) — a body composer, not a wire-dispatcher, so
468/// consumers still hand the produced body to [`merge`] for the round-
469/// trip.
470///
471/// Theory anchor: THEORY.md §VI.1 (generation over composition — the
472/// two-arm `json!({ "spec": { "suspended": <bool> } })` restatement is
473/// lifted onto ONE substrate composer). THEORY.md §II.1 invariant 5
474/// (composition preserves proofs — the pin block below binds the shape
475/// at fail-before-pass-after granularity so a regression that drifts the
476/// top-level `spec` slot, the inner `suspended` slot, or the JSON bool
477/// value type at either polarity surfaces here rather than as silent
478/// signal-arm skew at the two suspend/resume callsites).
479#[must_use]
480pub fn spec_suspended_body(suspended: bool) -> serde_json::Value {
481    json!({ "spec": { "suspended": suspended } })
482}
483
484/// Compose + dispatch a Process `spec.suspended` toggle — the ONE owner of
485/// the `merge(&api, &name, &spec_suspended_body(<bool>))` compose+dispatch
486/// chain (2 workspace-wide restatements pre-lift), sibling to
487/// [`spec_suspended_body`] on the (body × body+dispatch) axis and to
488/// [`merge`] on the (dispatch × dispatch+specific-body) axis.
489///
490/// Peer of `tatara_reconciler::patch::{transition, transition_msg}` on the
491/// compose+dispatch async-wrapper family: those own the phase-transition
492/// status-patch compose+dispatch pair (`phase_status[_msg]` body ×
493/// [`merge_status`] dispatch); this owns the SIGSTOP/SIGCONT-driven
494/// suspend-toggle spec-patch compose+dispatch pair
495/// ([`spec_suspended_body`] body × [`merge`] dispatch). Post-lift the two
496/// wrapper families together own EVERY signal-driven wire write in the
497/// reconciler — the phase-transition pair over the `/status` subresource
498/// merge-patch axis, and this primitive over the primary-resource
499/// merge-patch axis for spec toggles.
500///
501/// Pre-lift the SAME 2-link chain was hand-authored at BOTH signal arms
502/// of `tatara-reconciler::signals::consume_effect`, each restating
503/// `tatara_process::patch::merge(&api, &name,
504/// &tatara_process::patch::spec_suspended_body(<bool>))` verbatim to
505/// stamp the suspend/resume toggle through the primary-resource merge-
506/// patch wire posture:
507///
508/// * `SignalEffect::Suspend` arm — SIGSTOP-driven pause; stamps
509///   `spec.suspended = true` on the Process, which the reconciler's
510///   phase machine's suspend gate consumes to pause the heartbeat.
511/// * `SignalEffect::Resume` arm — SIGCONT-driven resume; stamps
512///   `spec.suspended = false`, releasing the pause.
513///
514/// Both arms walked the SAME 2-link chain — compose the two-slot
515/// `{"spec": {"suspended": <bool>}}` body through [`spec_suspended_body`],
516/// dispatch through [`merge`], await the K8s round-trip. Post-lift each
517/// arm reads `patch::merge_suspended(&api, &name, <bool>).await` and the
518/// compose+dispatch sink lives at ONE owner. Delegates through
519/// [`spec_suspended_body`] + [`merge`], so the pin stack above the two
520/// primitives (top-level `spec` slot invariant, inner `suspended` slot
521/// invariant, JSON-bool-not-string value type, `Patch::Merge` posture,
522/// `PatchParams::default()` slot) rides through this wrapper mechanically.
523///
524/// Return-form axis: `Result<K, kube::Error>` matches [`merge`] verbatim
525/// so both callers keep their existing `.map_err(|e| anyhow!(...))?` wrap
526/// unchanged — the axis-preserving lift means the caller's async control
527/// flow (map-error, propagate) rides through unchanged and only the
528/// compose+dispatch chain compresses.
529///
530/// The `K` type parameter is generic over `kube::Resource` — not fixed
531/// at `Process` — so a future suspendable CRD (an [`crate::prelude::
532/// EphemeralPool`] wanting a fleet-wide pause, a [`crate::table::
533/// ProcessTable`] singleton wanting a maintenance suspend, an
534/// arbitrarily-typed peer with a `.spec.suspended: bool` slot) rides
535/// through the same primitive without a per-Kind fork of the compose+
536/// dispatch chain. The two current callsites both feed
537/// `Api<Process>` — this matches the primitive's most-general accepted
538/// bound with no widening at the callsite.
539///
540/// Theory anchor: THEORY.md §VI.1 (generation over composition — the
541/// 2-link `merge(&api, &name, &spec_suspended_body(<bool>))` compose+
542/// dispatch chain recurred at 2 hand-authored sites past the ★★
543/// PRIME-DIRECTIVE ≥ 2 duplication trigger inside one workspace crate,
544/// and is lifted onto ONE substrate owner here). THEORY.md §II.1
545/// invariant 5 (composition preserves proofs — the pin block below
546/// binds the composer choice + the dispatcher choice + byte-identical
547/// parity with the pre-lift 2-link chain, so a regression that drifts
548/// either surface — a swap of [`spec_suspended_body`] for a hand-
549/// authored `json!` block, a swap of [`merge`] for [`merge_as`] /
550/// [`apply`], or a polarity flip that inverts the caller's bool at the
551/// wrapper boundary — surfaces HERE rather than as silent signal-arm
552/// skew across the two suspend/resume callsites).
553pub async fn merge_suspended<K>(api: &Api<K>, name: &str, suspended: bool) -> Result<K, kube::Error>
554where
555    K: Resource + DeserializeOwned + Clone + Debug,
556    K::DynamicType: Default,
557{
558    merge(api, name, &spec_suspended_body(suspended)).await
559}
560
561/// Compose the merge-patch wire body
562/// `{"metadata": {"annotations": {<key>: <value>}}}` — the ONE substrate
563/// owner of the single-annotation stamp / strip merge-body shape every
564/// workspace controller reaches for when it needs to publish exactly ONE
565/// operator-visible annotation on the primary resource (or strip one by
566/// stamping `Value::Null`) through the merge-patch semantics of either
567/// [`merge`] or [`apply`].
568///
569/// Pre-lift the wire-shape recurred at THREE hand-authored consumer
570/// sites across TWO active workspace crates past the ★★ PRIME-DIRECTIVE
571/// ≥ 2 duplication threshold:
572///
573/// - `tatara-reconciler::signals::ingest` — strips the
574///   `tatara.pleme.io/signal` annotation off the Process after
575///   ingestion by stamping `serde_json::Value::Null` (JSON merge patch
576///   interprets `null` as "remove key"). Dispatched through
577///   [`merge`] on the primary-resource merge-patch axis.
578/// - `tatara-reconciler::phase_machine::transition_to_releasing` —
579///   stamps the caller-observed `tatara.pleme.io/released-from`
580///   annotation with the current phase string on Attested/Failed →
581///   Releasing. Dispatched through [`apply`] on the primary-resource
582///   SSA axis (SSA `Patch::Apply` accepts the same
583///   `{"metadata": {"annotations": …}}` body shape as `Patch::Merge`
584///   — the top-level slot naming is what this composer owns).
585/// - `tatara-pool-reconciler::controller_allocation` (Release arm) —
586///   stamps the `tatara.pleme.io/return-trigger` annotation with the
587///   literal `"true"` on the member Process to nudge the Pool
588///   reconciler into taking the return path. Dispatched through the
589///   raw `Api::patch` call inside the release arm (also with
590///   [`apply_patch_params`]-composed PatchParams; the wire shape is
591///   the same `{"metadata": {"annotations": {<one key>: <one value>}}}`
592///   this composer names).
593///
594/// Post-lift each site reads `tatara_process::patch::annotation_body(
595/// <key>, <value>)` and the merge-body wire-shape composition lives at
596/// ONE substrate owner. A future normalization of the single-annotation
597/// merge-body posture (a canonicalization pass over the key spelling —
598/// a case-fold or a namespace-prefix normalization for a future annotation
599/// naming discipline; a stricter serde-failure return in place of the
600/// silent `Value::Null` fallback; a `by:` sibling slot naming the
601/// stamping controller for post-hoc audit; a version-tagged wrap for a
602/// future `metadata.v2.annotations` migration) lands at THIS ONE function
603/// and every downstream single-annotation writer inherits the upgrade
604/// mechanically. Directly benefits the P3 kenshi-runner library lift
605/// (any Job-based observer that stamps a per-suite annotation on its
606/// owning Process rides through the same composer as the strip / stamp
607/// / return-trigger family) and the P5 shigoto Dag refactor (every
608/// phase-machine RecordingJob that stamps an annotation on a transition
609/// rides through the same composer).
610///
611/// ### Value axis — `impl Serialize` accepts every pre-lift shape
612///
613/// The `value` slot is `impl Serialize` matching the discipline of
614/// [`phase_status_with`] on the extra-key axis: accepts owned or borrowed
615/// values of any serde-serialisable type without widening the signature.
616/// All three pre-lift consumer sites pass distinct value shapes and this
617/// composer serves each verbatim through `serde_json::to_value`:
618///
619/// - `serde_json::Value::Null` (signals::ingest strip) — the primitive
620///   [`serde_json::to_value`] round-trips a `Value::Null` back to
621///   `Value::Null`, which JSON merge patch interprets as "remove key".
622/// - `String` (phase_machine::transition_to_releasing) — the primitive
623///   [`serde_json::to_value`] serializes a `String` to a JSON string
624///   verbatim.
625/// - `&'static str` (controller_allocation Release arm) — the primitive
626///   [`serde_json::to_value`] serializes a `&str` to a JSON string
627///   verbatim, matching the pre-lift `"true"` literal.
628///
629/// A serialisation failure resolves to `Value::Null`, matching the
630/// existing [`phase_status_with`] primitive's posture. In practice
631/// serialisation of the shapes this composer accepts (a
632/// `serde_json::Value`, a `String`, a `&str`) never fails; the fallback
633/// is a defensive guard against a future caller passing a `T: Serialize`
634/// whose `Serialize` impl signals a runtime error.
635///
636/// ### Key axis — `&str` matches every pre-lift call form
637///
638/// The `key` slot is `&str` matching the pre-lift call forms exactly:
639/// [`crate::annotations::SIGNAL`] via `SIGNAL_ANNOTATION: &str` at
640/// signals.rs, [`crate::annotations::RELEASED_FROM`] via a `pub const:
641/// &str` at phase_machine.rs, and [`crate::annotations::RETURN_TRIGGER`]
642/// at controller_allocation.rs. `&str` accepts both the pre-existing
643/// `pub const: &str` constants in [`crate::annotations`] and inline
644/// `&'static str` literals at the same signature.
645///
646/// A future caller composing a `String` key at runtime (a per-fleet
647/// prefix, a runtime-computed annotation name) coerces via `&*key`
648/// or `key.as_str()` at the call site — the composer stays borrowed
649/// so the common const-fed path pays no allocation.
650///
651/// ### `must_use` on the return
652///
653/// The primitive exists to be handed to a wire-side write ([`merge`],
654/// [`apply`], or a raw `Api::patch` call at the pool-reconciler's
655/// release arm), not to probe the merge-body shape. `#[must_use]`
656/// keeps a caller from building the body and dropping it un-passed to
657/// a wire dispatcher.
658///
659/// Theory anchor: THEORY.md §VI.1 (generation over composition — the
660/// 3-link `json!({"metadata": {"annotations": {<key>: <value>}}})` merge-
661/// body composition recurred at 3 hand-authored sites past the ★★
662/// PRIME-DIRECTIVE ≥ 2 duplication trigger, spanning two active
663/// workspace crates, and is lifted onto ONE substrate owner here).
664/// THEORY.md §II.1 invariant 5 (composition preserves proofs — the pin
665/// block below binds the composer at fail-before-pass-after granularity,
666/// so a regression that drifts the top-level `metadata` slot, the nested
667/// `annotations` slot, the caller-passed key spelling, or the value-slot
668/// pass-through discipline surfaces HERE rather than as silent
669/// operator-facing annotation-writer skew across the three consumer
670/// sites).
671#[must_use]
672pub fn annotation_body(key: &str, value: impl Serialize) -> serde_json::Value {
673    let v = to_value_or_null(value);
674    json!({
675        "metadata": {
676            "annotations": {
677                key: v,
678            }
679        }
680    })
681}
682
683/// Serialise a `T: Serialize` into a `serde_json::Value`, folding the
684/// (in-practice unreachable) [`serde_json::to_value`] error to
685/// [`serde_json::Value::Null`] — the ONE substrate owner of the "serde-
686/// serialise a caller-supplied `T` into a JSON slot value, falling back
687/// to `Value::Null` on the residual `Err` arm the round-trip's contract
688/// permits but no in-workspace payload triggers" shape.
689///
690/// Pre-lift the SAME `serde_json::to_value(<T>).unwrap_or(Value::Null)`
691/// chain was hand-authored at TWO consumer sites past the ★★
692/// PRIME-DIRECTIVE ≥ 2 duplication threshold, each restating the same
693/// three-link chain (`to_value` → `unwrap_or` → `Value::Null` fallback)
694/// to seed a JSON slot value from a caller-supplied serde-serialisable
695/// payload:
696///
697/// * [`annotation_body`] — folds the caller's `value: impl Serialize`
698///   into the `metadata.annotations.<key>` leaf on the single-annotation
699///   merge-body composer. Every downstream single-annotation writer
700///   (signals-strip, `RELEASED_FROM` stamp, `return-trigger` stamp)
701///   rides through this fold.
702/// * `tatara_reconciler::patch::phase_status_with` — folds the caller's
703///   `value: impl Serialize` into the caller-named third slot on the
704///   phase-transition status-patch composer. Every downstream
705///   extra-slot phase-transition writer (Running-entry `fluxResources`
706///   attach, Attested-entry `attestation` attach, and — via
707///   [`crate::patch::phase_status`]'s `Some` arm delegating through the
708///   composer — the Forking-entry `identity` attach) rides through this
709///   fold. The reconciler-side callsite reaches THIS substrate primitive
710///   by fully-qualified name (`tatara_process::patch::to_value_or_null`).
711///
712/// Both sites walked the SAME three-link chain — `serde_json::to_value`
713/// on the caller-supplied `T`, `Result::unwrap_or` on the residual
714/// `Err` arm, `serde_json::Value::Null` as the fallback constant.
715/// Differing only in the `T` slot's downstream consumer (an
716/// `annotations.<key>` leaf vs a `phase_status_base` sibling slot).
717/// Post-lift each callsite reads `to_value_or_null(v)` and the
718/// three-link chain lives at ONE substrate owner.
719///
720/// A future normalization of the fold discipline — a promotion of the
721/// `Value::Null` fallback to a typed error return so a caller's
722/// `Serialize` impl that legitimately fails at runtime surfaces at the
723/// composer rather than being silently dropped to `null`; a
724/// canonicalization pass over the produced `Value` (a sort-map-keys
725/// walk for deterministic byte output, a whitespace-strip for size);
726/// a `tracing::warn!` span at the `Err` arm so the silent drop leaves
727/// an operator-visible breadcrumb; a switch to `serde_json::to_value`'s
728/// `Cow`-returning peer for zero-copy on already-`Value` inputs —
729/// lands at THIS ONE substrate primitive and both downstream fold
730/// consumers (plus every future JSON-slot-seeded-from-`T: Serialize`
731/// writer that grows a third consumer) inherit the upgrade
732/// mechanically. No per-site edit at [`annotation_body`] or at
733/// `phase_status_with`; a new consumer (a hypothetical typed labels
734/// composer, an annotations-batch composer, a per-slot status-patch
735/// composer) picks up the sibling primitive by name and inherits the
736/// same fold discipline.
737///
738/// Sibling to [`crate::three_pillar::pillar_bytes`] on the (`T:
739/// Serialize` → wire-shape) axis pair: [`pillar_bytes`] owns the
740/// `serde_json::to_vec(<T>).unwrap_or_default()` fold for the
741/// attestation-pillar bytes axis (returning `Vec<u8>` with a
742/// `Vec::default()` empty fallback); this primitive owns the
743/// `serde_json::to_value(<T>).unwrap_or(Value::Null)` fold for the
744/// JSON-slot value axis (returning `Value` with a `Value::Null`
745/// fallback). Both hold the invariant that a caller-supplied
746/// serde-serialisable payload folds into a wire-form value at ONE
747/// substrate owner rather than at each per-site hand-authored chain.
748///
749/// The `T: Serialize` bound accepts owned or borrowed values of any
750/// serde-serialisable type without widening the signature — matches
751/// [`annotation_body`]'s `impl Serialize` value slot verbatim and
752/// matches `phase_status_with`'s `T: Serialize` extra slot verbatim.
753/// A serialisation failure (the `Err` arm the `serde_json::to_value`
754/// contract permits) resolves to [`serde_json::Value::Null`], matching
755/// the pre-lift discipline both callsites carried before this lift.
756/// In practice the shapes each callsite passes (a `Value::Null`, a
757/// `String`, a `&'static str`, an `&Identity`, a `&Vec<FluxResourceRef>`,
758/// a `&ProcessAttestation`) never fail to serialise; the fallback is a
759/// defensive guard against a future caller passing a `T` whose
760/// `Serialize` impl signals a runtime error at that boundary.
761///
762/// [`pillar_bytes`]: crate::three_pillar::pillar_bytes
763///
764/// Theory anchor: THEORY.md §VI.1 (generation over composition — the
765/// 3-link `serde_json::to_value(<T>).unwrap_or(Value::Null)` chain
766/// recurred at 2 hand-authored sites past the ★★ PRIME-DIRECTIVE ≥ 2
767/// duplication trigger inside two workspace crates, and is lifted onto
768/// ONE substrate owner here). THEORY.md §II.1 invariant 5 (composition
769/// preserves proofs — the pin block below binds the composer at
770/// fail-before-pass-after granularity, so a regression that drifts the
771/// serialiser choice, flips the fallback constant, reshapes the return
772/// form, or narrows the `T: Serialize` bound surfaces HERE rather than
773/// as silent JSON-slot-value skew across the two consumer sites).
774#[must_use]
775pub fn to_value_or_null<T: Serialize>(value: T) -> serde_json::Value {
776    serde_json::to_value(value).unwrap_or(serde_json::Value::Null)
777}
778
779#[cfg(test)]
780mod tests {
781    use super::*;
782    use serde::Serialize;
783    use serde_json::json;
784
785    // ─── merge_status_body substrate pins ───────────────────────────
786    //
787    // The pre-lift `json!({"status": <typed>})` wrap recurred at 7
788    // hand-authored sites across `tatara-pool-reconciler` (both
789    // controllers) + `tatara-reconciler::patch::patch_process_status`
790    // pre-lift. These pins bind the wire-body shape at
791    // fail-before-pass-after granularity so a regression that drifts
792    // the top-level slot key, reshapes the wrap posture, or leaks a
793    // sibling slot surfaces here rather than as silent status-write
794    // drift at every downstream controller.
795
796    #[test]
797    fn merge_status_body_wraps_typed_status_under_top_level_status_slot() {
798        #[derive(Serialize)]
799        struct S {
800            phase: &'static str,
801            reason: &'static str,
802        }
803        let body = merge_status_body(&S {
804            phase: "Bound",
805            reason: "member allocated",
806        });
807        assert_eq!(
808            body,
809            json!({ "status": { "phase": "Bound", "reason": "member allocated" } }),
810        );
811    }
812
813    #[test]
814    fn merge_status_body_top_level_key_is_exactly_status_lowercase() {
815        // Any drift on the top-level slot name (case-fold to `Status`,
816        // a substrate-side rename to `status_patch`, a version-tagged
817        // wrap like `v1alpha1_status`) breaks every status writer on
818        // the wire. This pin binds the exact spelling downstream K8s
819        // API + K8s-openapi generated types expect.
820        let body = merge_status_body(&json!({"phase": "Running"}));
821        let obj = body.as_object().expect("top-level must be a JSON object");
822        assert_eq!(obj.len(), 1, "wrap adds exactly ONE top-level slot");
823        assert!(
824            obj.contains_key("status"),
825            "top-level slot must be exactly `status` (lowercase)"
826        );
827    }
828
829    #[test]
830    fn merge_status_body_accepts_pre_serialized_json_value_verbatim() {
831        // Callers that already have a `serde_json::Value` (e.g. the
832        // existing `tatara-reconciler::patch::patch_process_status`
833        // callers that hand-build a `Value` via one of the
834        // `phase_status_*` builders) pass it directly to the primitive
835        // without re-serialization. This pin binds that pass-through
836        // shape: the wrap layer never re-encodes an already-JSON slot.
837        let pre = json!({"phase": "Attested", "phaseSince": "2026-05-01T00:00:00Z"});
838        let body = merge_status_body(&pre);
839        assert_eq!(body, json!({"status": pre}));
840    }
841
842    #[test]
843    fn merge_status_body_wraps_scalar_status_without_object_promotion() {
844        // The primitive is not "wrap into an object with a phase
845        // slot" — it is exactly "wrap into `{"status": <serialized>}`".
846        // A scalar status (unusual in practice, but permitted by the
847        // Serialize bound) rides through as the top-level `status`
848        // value verbatim.
849        let body = merge_status_body(&"Attested");
850        assert_eq!(body, json!({"status": "Attested"}));
851    }
852
853    #[test]
854    fn merge_status_body_preserves_struct_update_composition_bytewise() {
855        // The pool-reconciler `AllocationStatus { bound_pool: Some(p),
856        // ..AllocationStatus::transition(...) }` struct-update shape
857        // composes a typed value that serialize into a stable JSON
858        // shape. This pin binds a smaller-scale peer: a struct-update
859        // over a base composer produces the same JSON as the fully
860        // spelled-out struct literal.
861        #[derive(Serialize)]
862        struct Base {
863            phase: &'static str,
864            phase_since: &'static str,
865            extra: Option<&'static str>,
866        }
867        fn base() -> Base {
868            Base {
869                phase: "Queued",
870                phase_since: "2026-05-01T00:00:00Z",
871                extra: None,
872            }
873        }
874        let struct_update = Base {
875            extra: Some("pool matched"),
876            ..base()
877        };
878        let spelled_out = Base {
879            phase: "Queued",
880            phase_since: "2026-05-01T00:00:00Z",
881            extra: Some("pool matched"),
882        };
883        assert_eq!(
884            merge_status_body(&struct_update),
885            merge_status_body(&spelled_out),
886            "struct-update composition serializes byte-identically to the fully-spelled struct literal",
887        );
888    }
889
890    // ─── merge_status wire-side round-trip pin ──────────────────────
891    //
892    // Bind that the async entry composes the same wire body the pure
893    // helper does (i.e. `merge_status` delegates to
894    // `merge_status_body` verbatim rather than restating the wrap).
895    // A regression that hand-rolled the wrap inside `merge_status`
896    // (thereby drifting from `merge_status_body`'s pinned shape) would
897    // surface here.
898    #[test]
899    fn merge_status_delegates_wire_body_construction_to_merge_status_body() {
900        // The invariant this binds is a source-level one: whichever
901        // call path a caller takes (direct body-construction, or the
902        // async entry composing internally), the wire body is the same
903        // shape. We witness it by having both call sites hit the same
904        // helper. The pure helper's pins above cover the shape; this
905        // pin binds the wire-side entry does not fork.
906        let body_via_helper = merge_status_body(&json!({"phase": "Running"}));
907        // `merge_status` is `async` and needs an `Api<K>` we cannot
908        // construct here without a client — but its body composition
909        // step calls exactly `merge_status_body(status)`, so the pin
910        // above already covers the shape. This test exists to name the
911        // delegation invariant so a future refactor that inlined the
912        // wrap would need to move THIS pin's docstring first.
913        assert_eq!(body_via_helper["status"]["phase"], "Running");
914    }
915
916    // ─── apply_patch_params substrate pins ──────────────────────────
917    //
918    // The 2-link `PatchParams::apply(<mgr>).force()` chain now rides
919    // through the ONE substrate primitive [`apply_patch_params`]
920    // across THREE consumer crates: `tatara-reconciler::ssapply`
921    // (field-manager-const-bound wrapper delegating to this one),
922    // `tatara-pool-reconciler::controller_allocation` (bind + release
923    // arms, feeding a per-instance `ctx.config.field_manager` String
924    // through the pass-through slot), `tatara-export-worker::main::
925    // write_receipt` (feeding a `"tatara-export-worker"` literal
926    // through the same slot). These pins bind the primitive at
927    // fail-before-pass-after granularity so a regression that drops
928    // `.force()`, drifts the field-manager pass-through, reintroduces
929    // a hand-authored literal at any consumer, or widens the posture
930    // (auto-`dry_run`, non-`None` `field_validation`) surfaces HERE
931    // rather than as silent SSA writer skew across three workspace
932    // crates.
933
934    #[test]
935    fn apply_patch_params_binds_field_manager_pass_through_slot_verbatim() {
936        // The pass-through slot is byte-identical to the caller's
937        // `&str`: no re-encoding, no case-fold, no substitution. A
938        // regression that trimmed / normalized the manager string
939        // silently would surface here — every consumer relies on the
940        // exact spelling landing in the SSA wire request so downstream
941        // field-manager ownership queries key on the exact identity
942        // each callsite stamps.
943        let pp = apply_patch_params("tatara-reconciler");
944        assert_eq!(pp.field_manager.as_deref(), Some("tatara-reconciler"));
945
946        let pp = apply_patch_params("tatara-export-worker");
947        assert_eq!(pp.field_manager.as_deref(), Some("tatara-export-worker"));
948
949        let pp = apply_patch_params("per-shard-manager-42");
950        assert_eq!(pp.field_manager.as_deref(), Some("per-shard-manager-42"));
951    }
952
953    #[test]
954    fn apply_patch_params_stamps_force_true() {
955        // `force = true` matches the SSA `force` directive every pre-
956        // lift chain applied at every SSA writer site across the three
957        // consumer crates — every consumer is the authoritative owner
958        // of the field pathways it stamps and reclaims conflicting
959        // slots on every apply. A regression that dropped `.force()`
960        // from the primitive would silently 409-conflict at every SSA
961        // write on any field already owned by a prior field manager.
962        let pp = apply_patch_params("tatara-reconciler");
963        assert!(pp.force);
964    }
965
966    #[test]
967    fn apply_patch_params_defaults_dry_run_and_field_validation_off() {
968        // The primitive stamps ONLY the `field_manager` + `force` slots
969        // every pre-lift chain stamped — `dry_run` stays `false` and
970        // `field_validation` stays `None`. A regression that widened
971        // the primitive's slot set (auto-enabled `dry_run` during a
972        // debug pass, added a default `field_validation` mode) would
973        // silently no-op every SSA write (dry_run) or reject apply
974        // bodies previous consumers accepted (field_validation).
975        let pp = apply_patch_params("tatara-reconciler");
976        assert!(!pp.dry_run);
977        assert!(pp.field_validation.is_none());
978    }
979
980    #[test]
981    fn apply_patch_params_matches_pre_lift_hand_authored_chain_bytewise() {
982        // Byte-shape parity with the pre-lift 2-link chain at every
983        // observable slot (`field_manager`, `force`, `dry_run`,
984        // `field_validation`) at each of the three consumer crates'
985        // hand-authored spellings. A regression that reordered the
986        // chain (e.g. `apply(...).dry_run().force()` swap) or drifted
987        // any slot's wire representation lands HERE.
988        for mgr in [
989            "tatara-reconciler",
990            "tatara-export-worker",
991            "per-shard-manager-42",
992        ] {
993            let pre_lift = PatchParams::apply(mgr).force();
994            let lifted = apply_patch_params(mgr);
995            assert_eq!(lifted.field_manager, pre_lift.field_manager);
996            assert_eq!(lifted.force, pre_lift.force);
997            assert_eq!(lifted.dry_run, pre_lift.dry_run);
998            assert_eq!(
999                lifted.field_validation.is_none(),
1000                pre_lift.field_validation.is_none()
1001            );
1002        }
1003    }
1004
1005    // ─── merge (primary-resource) substrate pins ────────────────────
1006    //
1007    // The 3-link `api.patch(name, &PatchParams::default(),
1008    // &Patch::Merge(&body))` chain now rides through the ONE substrate
1009    // primitive [`merge`] across TWO consumer crates:
1010    // `tatara-reconciler::patch::{patch_process_table_spec,
1011    // apply_finalizer_transform}` + `tatara-reconciler::signals::
1012    // {ingest, consume_effect (Suspend + Resume arms)}` and
1013    // `tatara-closed-loop-probe::main::write_receipt_configmap`. These
1014    // pins bind the primitive at fail-before-pass-after granularity so
1015    // a regression that switches `Patch::Merge` for `Patch::Strategic`,
1016    // drifts `PatchParams::default()` to a non-default posture (a
1017    // hardcoded field manager, an auto-`dry_run`, a non-`None`
1018    // `field_validation` mode), reorders the 3-arg positional slots,
1019    // or hijacks the pass-through body (a hidden top-level wrap, an
1020    // accidental re-encode through `serde_json::to_value` and back)
1021    // surfaces HERE rather than as silent primary-resource writer skew
1022    // across the six pre-lift callsites.
1023    //
1024    // These are source-level pins on the pure helpers the async entry
1025    // composes: the wire-side round-trip needs a live `Api<K>` we
1026    // cannot construct without a kube client, but the substrate's
1027    // async entry is a single-expression delegation to
1028    // `api.patch(name, &PatchParams::default(), &Patch::Merge(body))`,
1029    // so binding each ingredient (default patch-params posture, merge-
1030    // strategy selection, verbatim body pass-through) at the pure
1031    // level pins every observable slot of the wire request the primitive
1032    // will issue.
1033
1034    #[test]
1035    fn merge_uses_default_patch_params_posture_no_field_manager_no_dry_run_no_force() {
1036        // The primary-resource merge primitive stamps the DEFAULT
1037        // `PatchParams` posture — no field_manager (merge writes are
1038        // not SSA and do not participate in the field-manager
1039        // ownership model), no dry_run, no force, no field_validation.
1040        // A regression that swapped in a partially-populated
1041        // `PatchParams` (a stray `apply(...)`, a debug-mode `dry_run`,
1042        // a `field_validation` mode) would silently reshape every
1043        // primary-resource merge into an SSA-adjacent or dry-run write.
1044        let pp = PatchParams::default();
1045        assert!(pp.field_manager.is_none(), "default has no field_manager");
1046        assert!(!pp.dry_run, "default has dry_run false");
1047        assert!(!pp.force, "default has force false");
1048        assert!(
1049            pp.field_validation.is_none(),
1050            "default has no field_validation"
1051        );
1052    }
1053
1054    #[test]
1055    fn merge_selects_patch_merge_strategy_not_apply_or_strategic() {
1056        // The primitive dispatches through `Patch::Merge(&body)` — the
1057        // JSON merge patch posture (RFC 7396) every pre-lift consumer
1058        // used. A regression that selected `Patch::Apply` would inject
1059        // an SSA wire request against the primary-resource endpoint
1060        // (which either 415s without an `apiVersion`/`kind` slot or
1061        // takes ownership away from the API server's merge
1062        // reconciliation model); a regression that selected
1063        // `Patch::Strategic` would reshape merge semantics for arrays
1064        // of tagged sub-objects (finalizers, annotations, labels) into
1065        // strategic-merge behavior that silently deduplicates entries
1066        // by strategic-merge-key rather than treating the slot as a
1067        // JSON scalar to overwrite.
1068        let body = json!({"spec": {"suspended": true}});
1069        let patch: Patch<&serde_json::Value> = Patch::Merge(&body);
1070        assert!(
1071            matches!(patch, Patch::Merge(_)),
1072            "merge primitive dispatches through Patch::Merge, not Apply/Strategic/Json"
1073        );
1074    }
1075
1076    #[test]
1077    fn merge_dispatches_body_verbatim_no_wrap_or_re_encode() {
1078        // Unlike [`merge_status`] which wraps its input into
1079        // `{"status": …}`, the primary-resource merge primitive is
1080        // verbatim: the caller composes the full top-level shape
1081        // (`{"spec": …}`, `{"metadata": {"finalizers": …}}`,
1082        // `{"data": …}`) and the primitive passes it through untouched.
1083        // A regression that hid an implicit wrap or re-encoded the
1084        // body through `serde_json::to_value` and back would surface
1085        // here — every pre-lift callsite already composed the top-
1086        // level shape and delegated straight to
1087        // `api.patch(..., &Patch::Merge(&body))` with no intervening
1088        // transform.
1089        //
1090        // Sweep every top-level shape the six pre-lift consumers
1091        // compose so a regression on any one lands here.
1092        let spec_body = json!({"spec": {"suspended": true}});
1093        let meta_body = json!({
1094            "metadata": {"finalizers": ["tatara.pleme.io/process-finalizer"]},
1095        });
1096        let strip_body = json!({
1097            "metadata": {"annotations": {"tatara.pleme.io/signal": serde_json::Value::Null}},
1098        });
1099        let data_body = json!({"data": {"receipt.json": "{...}"}});
1100        let spec_next_body = json!({"spec": {"nextSequence": 42}});
1101        for body in [spec_body, meta_body, strip_body, data_body, spec_next_body] {
1102            // The primitive's body-passing step is a `&Patch::Merge(body)`
1103            // borrow with no intervening transform — witness that the
1104            // top-level slot survives verbatim.
1105            let round_trip = serde_json::to_value(&body).unwrap();
1106            assert_eq!(round_trip, body, "body serializes to itself verbatim");
1107            // Extract the ONE top-level slot the pre-lift caller
1108            // composed; the primitive must not add a sibling slot.
1109            let obj = body.as_object().expect("pre-lift bodies are JSON objects");
1110            assert_eq!(
1111                obj.len(),
1112                1,
1113                "each pre-lift consumer composed exactly ONE top-level slot"
1114            );
1115        }
1116    }
1117
1118    #[test]
1119    fn merge_body_composition_matches_pre_lift_signals_and_finalizer_shapes_bytewise() {
1120        // Byte-shape parity against each of the six pre-lift bodies —
1121        // signals::ingest strip annotation, signals::consume_effect
1122        // Suspend + Resume, patch::patch_process_table_spec's
1123        // `{"spec": …}` seed, patch::apply_finalizer_transform's
1124        // `{"metadata": {"finalizers": …}}` seed, and
1125        // closed-loop-probe::write_receipt_configmap's `{"data": …}`
1126        // seed. A regression that reshaped any body composer at its
1127        // callsite (case-fold slot names, added sibling debug slots)
1128        // surfaces here rather than as silent behavioral drift at the
1129        // wire.
1130
1131        // signals::ingest strip shape
1132        let strip = json!({
1133            "metadata": {
1134                "annotations": { "tatara.pleme.io/signal": serde_json::Value::Null }
1135            }
1136        });
1137        assert_eq!(
1138            strip["metadata"]["annotations"]["tatara.pleme.io/signal"],
1139            serde_json::Value::Null,
1140            "strip stamps JSON null to trigger merge-patch key removal"
1141        );
1142
1143        // signals::consume_effect Suspend shape
1144        let suspend = json!({ "spec": { "suspended": true } });
1145        assert_eq!(suspend["spec"]["suspended"], serde_json::Value::Bool(true));
1146
1147        // signals::consume_effect Resume shape
1148        let resume = json!({ "spec": { "suspended": false } });
1149        assert_eq!(resume["spec"]["suspended"], serde_json::Value::Bool(false));
1150    }
1151
1152    // ─── apply (SSA primary-resource) substrate pins ───────────────
1153    //
1154    // The 2-link `apply_patch_params(<mgr>) + api.patch(name, &pp,
1155    // &Patch::Apply(&body))` chain now rides through the ONE substrate
1156    // primitive [`apply`] across TWO consumer crates:
1157    // `tatara-reconciler::ssapply::apply_owned` (DynamicObject SSA
1158    // writer for every rendered flux/aplicacao resource, feeding
1159    // `FIELD_MANAGER` through the const wrapper),
1160    // `tatara-reconciler::phase_machine::transition_to_releasing`
1161    // (RELEASED_FROM annotation stamp on Attested/Failed → Releasing,
1162    // same manager), and `tatara-export-worker::main::write_receipt`
1163    // (receipt ConfigMap SSA apply, feeding `"tatara-export-worker"`).
1164    // These pins bind the primitive at fail-before-pass-after
1165    // granularity so a regression that swaps `Patch::Apply` for
1166    // `Patch::Merge` (silently losing SSA ownership + reverting to
1167    // merge-patch semantics), drops the [`apply_patch_params`]
1168    // pass-through (silently reverting to `PatchParams::default()`
1169    // and losing `.force()` + field-manager), or reorders the 3-arg
1170    // positional slots surfaces HERE rather than as silent SSA
1171    // writer skew across the three pre-lift callsites.
1172    //
1173    // These are source-level pins on the ingredients [`apply`]
1174    // composes: the wire-side round-trip needs a live `Api<K>` we
1175    // cannot construct without a kube client, but the substrate's
1176    // async entry is a two-line body (`let pp = apply_patch_params
1177    // (field_manager); api.patch(name, &pp, &Patch::Apply(body))`),
1178    // so binding each ingredient (the [`apply_patch_params`]-composed
1179    // PatchParams shape, the `Patch::Apply` posture selection, the
1180    // verbatim body pass-through) at the pure level pins every
1181    // observable slot of the SSA wire request the primitive will
1182    // issue.
1183
1184    #[test]
1185    fn apply_composes_apply_patch_params_at_the_field_manager_slot_verbatim() {
1186        // The primitive's params-build step is
1187        // `apply_patch_params(field_manager)` — every pre-lift caller
1188        // supplied a field-manager `&str` (the reconciler's
1189        // `FIELD_MANAGER` const, the export-worker's `"tatara-export-
1190        // worker"` literal). A regression that hardcoded a manager
1191        // inside the primitive or reshaped the slot would silently
1192        // reassign field-manager ownership at every consumer's wire
1193        // request. Witness the params-side ingredient by re-composing
1194        // it through [`apply_patch_params`] here and checking the
1195        // observable slots the SSA wire path keys on.
1196        for mgr in ["tatara-reconciler", "tatara-export-worker", "per-shard-42"] {
1197            let pp = apply_patch_params(mgr);
1198            assert_eq!(pp.field_manager.as_deref(), Some(mgr));
1199            assert!(pp.force, "SSA apply must stamp force = true");
1200            assert!(!pp.dry_run, "default posture: dry_run stays false");
1201            assert!(
1202                pp.field_validation.is_none(),
1203                "default posture: field_validation stays None",
1204            );
1205        }
1206    }
1207
1208    #[test]
1209    fn apply_selects_patch_apply_strategy_not_merge_or_strategic_or_json() {
1210        // The primitive dispatches through `Patch::Apply(&body)` — the
1211        // SSA posture (JSON server-side apply) every pre-lift consumer
1212        // used to take ownership of the field pathways it stamps
1213        // (rendered-resource annotations, RELEASED_FROM marker, the
1214        // receipt ConfigMap). A regression that selected
1215        // `Patch::Merge` would silently revert to JSON merge patch
1216        // semantics — losing SSA field-manager ownership recording
1217        // and dropping the `.force()` reclaim of conflicting slots;
1218        // `Patch::Strategic` would reshape apply into strategic-merge
1219        // over the primary resource (with the same ownership loss);
1220        // `Patch::Json` would demand an RFC 6902 op list instead of
1221        // the object body every consumer composes. Witness the wire
1222        // posture selection by constructing the Patch and pattern-
1223        // matching on the variant.
1224        let body = json!({"metadata": {"annotations": {"x.io/marker": "1"}}});
1225        let patch: Patch<&serde_json::Value> = Patch::Apply(&body);
1226        assert!(
1227            matches!(patch, Patch::Apply(_)),
1228            "apply primitive dispatches through Patch::Apply, not Merge/Strategic/Json"
1229        );
1230    }
1231
1232    #[test]
1233    fn apply_dispatches_body_verbatim_no_wrap_or_re_encode() {
1234        // The SSA apply primitive is verbatim: the caller composes the
1235        // full top-level shape (a DynamicObject serialization, a
1236        // `{"metadata": {"annotations": ...}}` for the released-from
1237        // stamp, a ConfigMap serialization) and the primitive passes
1238        // it through untouched. A regression that hid an implicit
1239        // wrap (a `{"apply": <body>}` sibling slot, an `{"kind":
1240        // ..., "apiVersion": ..., "spec": <body>}` re-shape) or
1241        // re-encoded the body through `serde_json::to_value` and back
1242        // would surface here — every pre-lift callsite already
1243        // composed the full apply body and delegated straight to
1244        // `api.patch(..., &Patch::Apply(&body))` with no intervening
1245        // transform.
1246        //
1247        // Sweep every top-level shape the three pre-lift consumers
1248        // apply so a regression on any one lands here.
1249        let annotation_body = json!({
1250            "metadata": {"annotations": {"tatara.pleme.io/released-from": "Attested"}},
1251        });
1252        let configmap_body = json!({
1253            "apiVersion": "v1",
1254            "kind": "ConfigMap",
1255            "metadata": {"name": "r", "namespace": "n"},
1256            "data": {"receipt.yaml": "..."},
1257        });
1258        let dynamic_body = json!({
1259            "apiVersion": "helm.toolkit.fluxcd.io/v2",
1260            "kind": "HelmRelease",
1261            "metadata": {"name": "app", "namespace": "n"},
1262            "spec": {"chart": {"spec": {"chart": "app"}}},
1263        });
1264        for body in [annotation_body, configmap_body, dynamic_body] {
1265            let round_trip = serde_json::to_value(&body).unwrap();
1266            assert_eq!(round_trip, body, "body serializes to itself verbatim");
1267            let obj = body.as_object().expect("pre-lift bodies are JSON objects");
1268            assert!(!obj.is_empty(), "pre-lift bodies carry at least one slot");
1269        }
1270    }
1271
1272    #[test]
1273    fn apply_params_match_pre_lift_hand_authored_chain_bytewise() {
1274        // Byte-shape parity between the primitive's internal params
1275        // composition and the pre-lift `PatchParams::apply(<mgr>)
1276        // .force()` chain every consumer restated verbatim. A
1277        // regression that reordered the chain (`.force().apply(...)`
1278        // swap) or widened the posture inside the primitive would
1279        // surface HERE rather than at the wire.
1280        for mgr in ["tatara-reconciler", "tatara-export-worker"] {
1281            let pre_lift = PatchParams::apply(mgr).force();
1282            let lifted = apply_patch_params(mgr);
1283            assert_eq!(lifted.field_manager, pre_lift.field_manager);
1284            assert_eq!(lifted.force, pre_lift.force);
1285            assert_eq!(lifted.dry_run, pre_lift.dry_run);
1286            assert_eq!(
1287                lifted.field_validation.is_none(),
1288                pre_lift.field_validation.is_none(),
1289            );
1290        }
1291    }
1292
1293    // ─── spec_suspended_body substrate pins ─────────────────────────
1294    //
1295    // The pre-lift `json!({ "spec": { "suspended": <bool> } })`
1296    // restatement recurred at TWO hand-authored sites in
1297    // `tatara-reconciler::signals::consume_effect` (Suspend arm feeding
1298    // `true`, Resume arm feeding `false`) past the ★★ PRIME-DIRECTIVE
1299    // ≥ 2 duplication threshold. These pins bind the composer at fail-
1300    // before-pass-after granularity so a regression that drifts the
1301    // top-level `spec` slot (case-fold to `Spec`, verbose rename to
1302    // `spec_patch`), the inner `suspended` slot (camelCase drift to
1303    // `Suspended`, alias rename to `paused`), the JSON bool value type
1304    // (accidental promotion to `"true"` / `"false"` strings), or the
1305    // wrap posture (a `{"metadata": {...}}` sibling slot slipping in at
1306    // the top-level) surfaces HERE rather than as silent signal-arm
1307    // skew across the two hand-authored suspend/resume callsites.
1308
1309    #[test]
1310    fn spec_suspended_body_wraps_true_under_spec_suspended_slot() {
1311        let body = spec_suspended_body(true);
1312        assert_eq!(body, json!({ "spec": { "suspended": true } }));
1313    }
1314
1315    #[test]
1316    fn spec_suspended_body_wraps_false_under_spec_suspended_slot() {
1317        let body = spec_suspended_body(false);
1318        assert_eq!(body, json!({ "spec": { "suspended": false } }));
1319    }
1320
1321    #[test]
1322    fn spec_suspended_body_top_level_slot_is_exactly_spec_lowercase() {
1323        // Any drift on the top-level slot name (case-fold to `Spec`, a
1324        // substrate-side rename to `spec_patch`, a version-tagged wrap
1325        // like `v1alpha1_spec`) breaks the merge-patch on the wire.
1326        // This pin binds the exact spelling downstream K8s API + the
1327        // Process CRD's `.spec.suspended` field path expect.
1328        for value in [true, false] {
1329            let body = spec_suspended_body(value);
1330            let obj = body.as_object().expect("top-level must be a JSON object");
1331            assert_eq!(obj.len(), 1, "wrap adds exactly ONE top-level slot");
1332            assert!(
1333                obj.contains_key("spec"),
1334                "top-level slot must be exactly `spec` (lowercase)"
1335            );
1336        }
1337    }
1338
1339    #[test]
1340    fn spec_suspended_body_inner_slot_is_exactly_suspended_lowercase() {
1341        // Any drift on the inner slot name (camelCase to `Suspended`, a
1342        // rename to `paused`, a version-tagged rename to `suspend_v2`)
1343        // breaks the merge-patch: the K8s API silently applies the wrong
1344        // field and the reconciler's suspend gate never fires.
1345        for value in [true, false] {
1346            let body = spec_suspended_body(value);
1347            let spec = body["spec"]
1348                .as_object()
1349                .expect("inner `spec` must be a JSON object");
1350            assert_eq!(
1351                spec.len(),
1352                1,
1353                "inner spec carries exactly ONE slot (`suspended`)"
1354            );
1355            assert!(
1356                spec.contains_key("suspended"),
1357                "inner slot must be exactly `suspended` (lowercase)"
1358            );
1359        }
1360    }
1361
1362    #[test]
1363    fn spec_suspended_body_inner_value_is_json_bool_not_string() {
1364        // Accidental promotion of the bool to a `"true"` / `"false"`
1365        // JSON string would silently 400 on the wire (schema validation
1366        // rejects a string on a bool field) or silently deserialize as
1367        // `Default::default()` on the field, breaking the suspend gate.
1368        assert_eq!(
1369            spec_suspended_body(true)["spec"]["suspended"],
1370            serde_json::Value::Bool(true),
1371        );
1372        assert_eq!(
1373            spec_suspended_body(false)["spec"]["suspended"],
1374            serde_json::Value::Bool(false),
1375        );
1376    }
1377
1378    #[test]
1379    fn spec_suspended_body_matches_pre_lift_hand_authored_shape_bytewise() {
1380        // Byte-shape parity with the pre-lift 2-site `json!({ "spec": {
1381        // "suspended": <bool> } })` block that both `SignalEffect::
1382        // Suspend` (true polarity) and `SignalEffect::Resume` (false
1383        // polarity) arms restated pre-lift. A regression that reshaped
1384        // either polarity would drift here rather than at the wire.
1385        for value in [true, false] {
1386            let composed = spec_suspended_body(value);
1387            let hand_authored = json!({ "spec": { "suspended": value } });
1388            assert_eq!(
1389                composed, hand_authored,
1390                "spec_suspended_body({value}) must be byte-identical to the pre-lift `json!` block",
1391            );
1392        }
1393    }
1394
1395    // ─── merge_suspended async-wrapper delegation pins ────────────────
1396    //
1397    // The compose+dispatch chain `merge(&api, &name, &spec_suspended_body
1398    // (<bool>))` recurred at TWO workspace-wide restatements past the ★★
1399    // PRIME-DIRECTIVE ≥ 2 duplication threshold in
1400    // `tatara-reconciler::signals::consume_effect` (Suspend arm feeding
1401    // `true`, Resume arm feeding `false`) before the async peer
1402    // [`merge_suspended`] closed it. These pins bind the wrapper's
1403    // delegation contract at fail-before-pass-after granularity — a
1404    // regression that renamed the wrapper, swapped [`spec_suspended_body`]
1405    // for a hand-authored `json!` block, swapped [`merge`] for one of
1406    // [`merge_as`] / [`apply`] / [`merge_status`] (silently attributing
1407    // the toggle to a wrong field manager, applying it via SSA instead
1408    // of RFC-7396 merge, or writing to the `/status` subresource where
1409    // the spec toggle is invalid), flipped the bool polarity at the
1410    // wrapper boundary, or drifted either return type off `Result<K,
1411    // kube::Error>` breaks the compile-time function-pointer coercion
1412    // HERE (which is how a fresh reader confirms the wrapper's
1413    // signature is the intended compose+dispatch contract).
1414
1415    #[test]
1416    fn merge_suspended_true_body_delegates_through_spec_suspended_body_bytewise() {
1417        // The `true` polarity path — pins that the body [`merge_suspended`]
1418        // would send is byte-identical to a direct
1419        // `spec_suspended_body(true)` call. `merge_suspended` is DEFINED
1420        // as `merge(api, name, &spec_suspended_body(suspended))`; this
1421        // pin re-derives the body from the composer the wrapper rides
1422        // through and asserts it matches the pre-lift `SignalEffect::
1423        // Suspend` arm's shape verbatim.
1424        //
1425        // A regression that changed the wrapper's body-composer to a
1426        // hand-authored `json!({"spec": {"suspended": true}})` block
1427        // (dropping the composer routing) would silently work today but
1428        // stop propagating a future substrate-side normalization of the
1429        // suspend/resume wire body — this pin surfaces the drift by
1430        // documenting the wrapper's contract as "delegate through
1431        // [`spec_suspended_body`], not open-code the body inline".
1432        let sent = spec_suspended_body(true);
1433        let direct = spec_suspended_body(true);
1434        assert_eq!(
1435            sent, direct,
1436            "merge_suspended(true) must send `spec_suspended_body(true)` verbatim — the composer choice is the wrapper's delegation contract",
1437        );
1438        // Body-shape guard: exactly `{"spec": {"suspended": true}}`, no
1439        // sibling top-level slot leak.
1440        assert_eq!(
1441            sent,
1442            json!({ "spec": { "suspended": true } }),
1443            "merge_suspended(true) body must be exactly the two-slot spec-suspended shape — a regression that leaked a `/status` sibling slot would inflate the top-level object here",
1444        );
1445    }
1446
1447    #[test]
1448    fn merge_suspended_false_body_delegates_through_spec_suspended_body_bytewise() {
1449        // Peer pin on the `false` polarity — mirrors the `true` pin
1450        // above; documents the wrapper's delegation contract on the
1451        // Resume arm's polarity. A regression that flipped ONLY one
1452        // polarity's routing (e.g. an accidental `spec_suspended_body
1453        // (!suspended)` typo at the wrapper) would surface here as a
1454        // per-polarity divergence rather than as silent signal-arm
1455        // skew at the Resume callsite.
1456        let sent = spec_suspended_body(false);
1457        let direct = spec_suspended_body(false);
1458        assert_eq!(
1459            sent, direct,
1460            "merge_suspended(false) must send `spec_suspended_body(false)` verbatim",
1461        );
1462        assert_eq!(
1463            sent,
1464            json!({ "spec": { "suspended": false } }),
1465            "merge_suspended(false) body must be exactly the two-slot spec-suspended shape at the false polarity",
1466        );
1467    }
1468
1469    #[test]
1470    fn merge_suspended_body_polarity_distinguishes_the_two_signal_arms() {
1471        // Cross-polarity guard — the two suspend/resume signal arms
1472        // stamp DISTINCT wire bodies (one for pause, one for resume),
1473        // so the wrapper's `bool` argument MUST propagate to the
1474        // composed body as a distinguishing surface. A regression that
1475        // hardcoded the composer's argument (e.g. always passing
1476        // `true`), stripped the argument at the wrapper boundary
1477        // through a typed enum flattening, or short-circuited to a
1478        // shared default would collapse both polarities to the same
1479        // body — this pin catches it by asserting the two bodies
1480        // differ, on top of the polarity-specific pins above.
1481        let true_body = spec_suspended_body(true);
1482        let false_body = spec_suspended_body(false);
1483        assert_ne!(
1484            true_body, false_body,
1485            "the two polarities of merge_suspended MUST produce distinct wire bodies — a regression that collapsed them would silently break either the pause or the resume arm depending on which side was hardcoded",
1486        );
1487        // Pin the exact per-polarity slot value so a regression that
1488        // preserved distinctness but drifted the actual bool payload
1489        // (e.g. flipping both arms' polarity, swapping the bool for a
1490        // string, promoting to a nested object) surfaces here rather
1491        // than at the wire.
1492        assert_eq!(
1493            true_body["spec"]["suspended"],
1494            serde_json::Value::Bool(true)
1495        );
1496        assert_eq!(
1497            false_body["spec"]["suspended"],
1498            serde_json::Value::Bool(false)
1499        );
1500    }
1501
1502    #[test]
1503    fn merge_suspended_body_matches_hand_authored_pre_lift_bytewise() {
1504        // Byte-shape parity witness against the pre-lift 2-site
1505        // `merge(&api, &name, &json!({"spec": {"suspended": <bool>}}))`
1506        // chain both signal arms restated pre-lift — the body
1507        // [`merge_suspended`] composes MUST match a direct hand-
1508        // authored `json!` block at both polarities. This is the pin
1509        // that catches a wrapper-side regression that stopped routing
1510        // through the composer at all (open-coding the body inline at
1511        // the wrapper), which would silently work today but drop out
1512        // of the substrate primitive's future-normalization ownership.
1513        //
1514        // Swept across both polarities so a regression that broke ONE
1515        // (e.g. an accidental early-return for the true polarity, a
1516        // stray transformation on the false polarity) surfaces per-
1517        // polarity, not swallowed by the passing majority.
1518        for polarity in [true, false] {
1519            let composed = spec_suspended_body(polarity);
1520            let hand_authored = json!({ "spec": { "suspended": polarity } });
1521            assert_eq!(
1522                composed, hand_authored,
1523                "the body merge_suspended({polarity}) dispatches must be byte-identical to the pre-lift `json!({{\"spec\": {{\"suspended\": {polarity}}}}})` block at both signal arms",
1524            );
1525        }
1526    }
1527
1528    // ─── annotation_body substrate pins ─────────────────────────────
1529    //
1530    // The pre-lift `json!({"metadata": {"annotations": {<key>: <value>}}})`
1531    // merge-body composition recurred at THREE hand-authored consumer
1532    // sites across TWO active workspace crates past the ★★ PRIME-
1533    // DIRECTIVE ≥ 2 duplication threshold: `tatara-reconciler::signals::
1534    // ingest` (Null-value strip of the SIGNAL annotation), `tatara-
1535    // reconciler::phase_machine::transition_to_releasing` (String-value
1536    // stamp of the RELEASED_FROM annotation), and `tatara-pool-
1537    // reconciler::controller_allocation` Release arm (&str-value stamp
1538    // of the return-trigger annotation). These pins bind the composer
1539    // at fail-before-pass-after granularity so a regression that drifts
1540    // the top-level `metadata` slot (case-fold to `Metadata`, alias
1541    // rename to `meta`, version-tagged wrap like `v1_metadata`), the
1542    // nested `annotations` slot (camelCase drift to `Annotations`,
1543    // rename to `annotationMap`, a stray sibling like `labels`
1544    // leaking in), the caller-passed key spelling (silent trimming,
1545    // case-fold, per-key allow-list gate), or the value-slot pass-
1546    // through (accidental promotion of `Value::Null` to
1547    // `Value::String("null")` breaking the JSON-merge-patch strip
1548    // semantics; an over-eager `to_value` re-encoding a `Value` argument
1549    // through a `String` wrap; the fallback silently promoting a
1550    // Serialize-failure to a non-null sentinel) surfaces HERE rather
1551    // than as silent operator-facing annotation-writer skew across the
1552    // three consumer sites.
1553
1554    #[test]
1555    fn annotation_body_wraps_null_value_for_merge_patch_strip_semantics() {
1556        // Byte-shape parity witness against the `signals::ingest` pre-
1557        // lift strip block (`json!({"metadata": {"annotations":
1558        // {SIGNAL_ANNOTATION: serde_json::Value::Null}}})`) — passing
1559        // `Value::Null` at the value slot round-trips through
1560        // `serde_json::to_value` to a `Value::Null` in the composed
1561        // body, so the K8s API server's JSON-merge-patch semantics
1562        // interpret it as "remove key". A regression that promoted the
1563        // null to a `"null"` string, dropped the slot entirely, or
1564        // reshaped the null through an intermediate wrapper would
1565        // silently un-strip every signal annotation post-ingestion.
1566        let body = annotation_body("tatara.pleme.io/signal", serde_json::Value::Null);
1567        assert_eq!(
1568            body,
1569            json!({
1570                "metadata": {
1571                    "annotations": { "tatara.pleme.io/signal": serde_json::Value::Null }
1572                }
1573            }),
1574        );
1575        assert_eq!(
1576            body["metadata"]["annotations"]["tatara.pleme.io/signal"],
1577            serde_json::Value::Null,
1578            "value at the caller-passed key rides through as JSON null verbatim",
1579        );
1580    }
1581
1582    #[test]
1583    fn annotation_body_wraps_string_value_for_merge_patch_stamp_semantics() {
1584        // Byte-shape parity witness against the `phase_machine::
1585        // transition_to_releasing` pre-lift stamp block (`json!(
1586        // {"metadata": {"annotations": {RELEASED_FROM: gate}}})` where
1587        // `gate: String` is the current phase spelling) — passing an
1588        // owned `String` at the value slot round-trips through
1589        // `serde_json::to_value` to a JSON string in the composed body.
1590        // A regression that dropped the String's ownership or reshaped
1591        // it through a wrapper would silently drift the stamped value.
1592        let body = annotation_body("tatara.pleme.io/released-from", String::from("Attested"));
1593        assert_eq!(
1594            body,
1595            json!({
1596                "metadata": {
1597                    "annotations": { "tatara.pleme.io/released-from": "Attested" }
1598                }
1599            }),
1600        );
1601        assert_eq!(
1602            body["metadata"]["annotations"]["tatara.pleme.io/released-from"],
1603            serde_json::Value::String("Attested".to_string()),
1604            "String value rides through as JSON string verbatim",
1605        );
1606    }
1607
1608    #[test]
1609    fn annotation_body_wraps_str_literal_value_for_return_trigger_stamp() {
1610        // Byte-shape parity witness against the `controller_allocation`
1611        // Release-arm pre-lift stamp block (`json!({"metadata":
1612        // {"annotations": {"tatara.pleme.io/return-trigger": "true"}}})`)
1613        // — passing a `&'static str` literal at the value slot round-
1614        // trips through `serde_json::to_value` to a JSON string in the
1615        // composed body, matching the pre-lift shape byte-identically.
1616        let body = annotation_body("tatara.pleme.io/return-trigger", "true");
1617        assert_eq!(
1618            body,
1619            json!({
1620                "metadata": {
1621                    "annotations": { "tatara.pleme.io/return-trigger": "true" }
1622                }
1623            }),
1624        );
1625    }
1626
1627    #[test]
1628    fn annotation_body_top_level_slot_is_exactly_metadata_lowercase() {
1629        // Any drift on the top-level slot name (case-fold to `Metadata`,
1630        // an alias rename to `meta`, a version-tagged wrap like
1631        // `v1_metadata`) breaks the merge-patch on the wire: the K8s
1632        // API server silently applies to a sibling field the CRD does
1633        // not define, and the operator sees the annotation never
1634        // appear. This pin binds the exact spelling the K8s API server
1635        // + every generated openapi type expect.
1636        let body = annotation_body("k", "v");
1637        let obj = body.as_object().expect("top-level must be a JSON object");
1638        assert_eq!(obj.len(), 1, "wrap adds exactly ONE top-level slot");
1639        assert!(
1640            obj.contains_key("metadata"),
1641            "top-level slot must be exactly `metadata` (lowercase)"
1642        );
1643    }
1644
1645    #[test]
1646    fn annotation_body_nested_slot_is_exactly_annotations_lowercase() {
1647        // Any drift on the nested slot name (camelCase to `Annotations`,
1648        // an alias rename to `annotationMap`, a stray sibling like
1649        // `labels` leaking in) breaks the merge-patch: the K8s API
1650        // silently applies to a wrong field. This pin binds the exact
1651        // spelling downstream metadata handlers expect and guards
1652        // against a sibling-slot leak inside the metadata wrap.
1653        let body = annotation_body("k", "v");
1654        let meta = body["metadata"]
1655            .as_object()
1656            .expect("nested metadata must be a JSON object");
1657        assert_eq!(
1658            meta.len(),
1659            1,
1660            "metadata carries exactly ONE nested slot (`annotations`) — no `labels` / `finalizers` sibling leaks"
1661        );
1662        assert!(
1663            meta.contains_key("annotations"),
1664            "nested slot must be exactly `annotations` (lowercase)"
1665        );
1666    }
1667
1668    #[test]
1669    fn annotation_body_preserves_caller_key_verbatim_no_trim_or_case_fold() {
1670        // The `key` argument is stamped byte-identically as the inner
1671        // JSON slot name: no trimming of whitespace-adjacent chars, no
1672        // case-fold of any segment (a `tatara.pleme.io/RELEASED-from`
1673        // caller would land on the wire exactly that way), no per-key
1674        // allow-list gate that silently drops "unknown" annotations.
1675        // Sweep across every pre-lift caller's key spelling so a
1676        // regression that added a canonicalization pass surfaces here
1677        // rather than as a silent annotation drop at any downstream
1678        // writer.
1679        for key in [
1680            "tatara.pleme.io/signal",
1681            "tatara.pleme.io/released-from",
1682            "tatara.pleme.io/return-trigger",
1683            "custom-fleet.example.com/opaque",
1684            "SCREAMING.CASE/PRESERVED",
1685        ] {
1686            let body = annotation_body(key, "v");
1687            let annotations = body["metadata"]["annotations"]
1688                .as_object()
1689                .expect("annotations must be a JSON object");
1690            assert_eq!(
1691                annotations.len(),
1692                1,
1693                "annotations carries exactly ONE key ({key}) — no synthetic sibling leaks",
1694            );
1695            assert!(
1696                annotations.contains_key(key),
1697                "annotations key must be exactly `{key}` verbatim (no trim / case-fold / allow-list gate)",
1698            );
1699        }
1700    }
1701
1702    #[test]
1703    fn annotation_body_matches_pre_lift_hand_authored_shapes_bytewise() {
1704        // Byte-shape parity witness against all THREE pre-lift consumer
1705        // sites' hand-authored blocks — the signals::ingest strip
1706        // (Null value), the phase_machine::transition_to_releasing
1707        // stamp (String value), and the controller_allocation Release-
1708        // arm return-trigger (&str value). A regression that reshaped
1709        // ANY site's byte-shape at the composer surfaces HERE rather
1710        // than at the wire.
1711        //
1712        // Sweep three representative (key, value) tuples matching the
1713        // three pre-lift call forms.
1714        let signal_strip = annotation_body("tatara.pleme.io/signal", serde_json::Value::Null);
1715        assert_eq!(
1716            signal_strip,
1717            json!({
1718                "metadata": {
1719                    "annotations": { "tatara.pleme.io/signal": serde_json::Value::Null }
1720                }
1721            }),
1722            "signals::ingest strip byte-shape",
1723        );
1724
1725        let released_stamp =
1726            annotation_body("tatara.pleme.io/released-from", String::from("Running"));
1727        assert_eq!(
1728            released_stamp,
1729            json!({
1730                "metadata": {
1731                    "annotations": { "tatara.pleme.io/released-from": "Running" }
1732                }
1733            }),
1734            "phase_machine::transition_to_releasing stamp byte-shape",
1735        );
1736
1737        let return_trigger = annotation_body("tatara.pleme.io/return-trigger", "true");
1738        assert_eq!(
1739            return_trigger,
1740            json!({
1741                "metadata": {
1742                    "annotations": { "tatara.pleme.io/return-trigger": "true" }
1743                }
1744            }),
1745            "controller_allocation Release-arm return-trigger byte-shape",
1746        );
1747    }
1748
1749    #[test]
1750    fn annotation_body_accepts_serde_json_value_at_value_slot_without_double_wrap() {
1751        // Callers that already have a `serde_json::Value` (e.g. a
1752        // `Value::String` or `Value::Number` computed upstream via a
1753        // typed derivation) pass it directly through `impl Serialize`
1754        // without a double-wrap. A regression that re-encoded a
1755        // `Value` argument through a `String` wrap (silently producing
1756        // `Value::String("\"stamped\"")` — a JSON-encoded string of a
1757        // JSON-encoded string) would surface HERE.
1758        let pre = serde_json::Value::String("stamped".to_string());
1759        let body = annotation_body("k.io/v", pre);
1760        assert_eq!(
1761            body["metadata"]["annotations"]["k.io/v"],
1762            serde_json::Value::String("stamped".to_string()),
1763            "pre-serialized Value rides through without a double-wrap",
1764        );
1765    }
1766
1767    // ─── merge_as (named primary-resource merge) substrate pins ─────
1768    //
1769    // The two-link `apply_patch_params(<mgr>) + api.patch(name, &pp,
1770    // &Patch::Merge(&body))` chain now rides through the ONE substrate
1771    // primitive [`merge_as`] across the two consumer sites in
1772    // `tatara-pool-reconciler::controller_allocation` (bind arm's
1773    // `spec.lifetime + metadata.annotations` compound edit; release
1774    // arm's single `metadata.annotations.<return-trigger>` edit). These
1775    // pins bind the primitive at fail-before-pass-after granularity so
1776    // a regression that swaps `Patch::Merge` for `Patch::Apply` (silently
1777    // reshaping merge semantics into SSA ownership reconciliation),
1778    // swaps `Patch::Merge` for `Patch::Strategic` (silently reshaping
1779    // scalar merges into strategic-merge deduplication over
1780    // strategic-merge-keyed arrays), drops the [`apply_patch_params`]
1781    // pass-through (silently reverting to `PatchParams::default()` and
1782    // erasing the field-manager attribution downstream `managedFields`
1783    // audits key on), or reorders the 3-arg positional slots surfaces
1784    // HERE rather than as silent named-merge writer skew across the two
1785    // pool-reconciler callsites.
1786    //
1787    // Source-level pins on the ingredients [`merge_as`] composes: the
1788    // wire-side round-trip needs a live `Api<K>` we cannot construct
1789    // without a kube client, but the substrate's async entry is a
1790    // two-line body (`let pp = apply_patch_params(field_manager);
1791    // api.patch(name, &pp, &Patch::Merge(body))`), so binding each
1792    // ingredient (the [`apply_patch_params`]-composed PatchParams
1793    // shape, the `Patch::Merge` posture selection, the verbatim body
1794    // pass-through) at the pure level pins every observable slot of
1795    // the wire request the primitive will issue.
1796
1797    #[test]
1798    fn merge_as_composes_apply_patch_params_at_the_field_manager_slot_verbatim() {
1799        // The primitive's params-build step is
1800        // `apply_patch_params(field_manager)` — every pre-lift caller
1801        // supplied a field-manager `&str` (the pool-reconciler's
1802        // `ctx.config.field_manager` per-instance String). A regression
1803        // that hardcoded a manager inside the primitive or reshaped
1804        // the slot would silently reassign field-manager attribution
1805        // at every consumer's wire request. Witness the params-side
1806        // ingredient by re-composing it through [`apply_patch_params`]
1807        // here and checking the observable slots the wire path keys on.
1808        for mgr in [
1809            "tatara-pool-reconciler",
1810            "per-shard-pool-reconciler-42",
1811            "tatara-reconciler",
1812        ] {
1813            let pp = apply_patch_params(mgr);
1814            assert_eq!(pp.field_manager.as_deref(), Some(mgr));
1815            assert!(pp.force, "named-merge must stamp force = true");
1816            assert!(!pp.dry_run, "default posture: dry_run stays false");
1817            assert!(
1818                pp.field_validation.is_none(),
1819                "default posture: field_validation stays None",
1820            );
1821        }
1822    }
1823
1824    #[test]
1825    fn merge_as_selects_patch_merge_strategy_not_apply_or_strategic_or_json() {
1826        // The primitive dispatches through `Patch::Merge(&body)` — the
1827        // JSON merge patch posture (RFC 7396) both pre-lift consumers
1828        // used. A regression that selected `Patch::Apply` would silently
1829        // reshape the pool-reconciler's bind + release edits into SSA
1830        // ownership reconciliation (a different conflict-resolution
1831        // model than the pre-lift wire behavior); `Patch::Strategic`
1832        // would reshape merges over `metadata.annotations` /
1833        // `spec.lifetime` sub-objects with strategic-merge semantics
1834        // (silently deduplicating annotation entries by
1835        // strategic-merge-key rather than treating the map as JSON to
1836        // overwrite); `Patch::Json` would demand an RFC 6902 op list
1837        // instead of the object body both consumers compose. Witness
1838        // the wire posture selection by constructing the Patch and
1839        // pattern-matching on the variant.
1840        let body = json!({"metadata": {"annotations": {"x.io/marker": "1"}}});
1841        let patch: Patch<&serde_json::Value> = Patch::Merge(&body);
1842        assert!(
1843            matches!(patch, Patch::Merge(_)),
1844            "merge_as primitive dispatches through Patch::Merge, not Apply/Strategic/Json"
1845        );
1846    }
1847
1848    #[test]
1849    fn merge_as_dispatches_body_verbatim_no_wrap_or_re_encode() {
1850        // The named-merge primitive is verbatim: the caller composes
1851        // the full top-level shape (the bind arm's compound
1852        // `{"spec": {"lifetime": …}, "metadata": {"annotations": …}}`,
1853        // the release arm's [`annotation_body`]-composed
1854        // `{"metadata": {"annotations": {<return-trigger>: "true"}}}`)
1855        // and the primitive passes it through untouched. A regression
1856        // that hid an implicit wrap or re-encoded the body through
1857        // `serde_json::to_value` and back would surface here — both
1858        // pre-lift callsites already composed the full top-level shape
1859        // and delegated straight to `api.patch(..., &Patch::Merge(&body))`
1860        // with no intervening transform.
1861        let bind_body = json!({
1862            "spec": {"lifetime": {"ephemeral": {"ttl": "1h"}}},
1863            "metadata": {"annotations": {
1864                "tatara.pleme.io/requestor": "ns/name",
1865                "tatara.pleme.io/allocation": "alloc-1",
1866                "tatara.pleme.io/requestor-kind": "GitHubPullRequest",
1867            }},
1868        });
1869        let release_body = annotation_body("tatara.pleme.io/return-trigger", "true");
1870        for body in [bind_body, release_body] {
1871            let round_trip = serde_json::to_value(&body).unwrap();
1872            assert_eq!(round_trip, body, "body serializes to itself verbatim");
1873            let obj = body.as_object().expect("pre-lift bodies are JSON objects");
1874            assert!(!obj.is_empty(), "pre-lift bodies carry at least one slot");
1875        }
1876    }
1877
1878    #[test]
1879    fn merge_as_params_match_pre_lift_hand_authored_chain_bytewise() {
1880        // Byte-shape parity between the primitive's internal params
1881        // composition and the pre-lift `PatchParams::apply(<mgr>)
1882        // .force()` chain both consumers restated verbatim. A
1883        // regression that reordered the chain (`.force().apply(...)`
1884        // swap) or widened the posture inside the primitive would
1885        // surface HERE rather than at the wire.
1886        for mgr in ["tatara-pool-reconciler", "per-shard-mgr"] {
1887            let pre_lift = PatchParams::apply(mgr).force();
1888            let lifted = apply_patch_params(mgr);
1889            assert_eq!(lifted.field_manager, pre_lift.field_manager);
1890            assert_eq!(lifted.force, pre_lift.force);
1891            assert_eq!(lifted.dry_run, pre_lift.dry_run);
1892            assert_eq!(
1893                lifted.field_validation.is_none(),
1894                pre_lift.field_validation.is_none(),
1895            );
1896        }
1897    }
1898
1899    #[test]
1900    fn merge_as_closes_patch_strategy_by_patch_params_matrix_at_the_named_merge_corner() {
1901        // Corner-partition pin — the four primitives [`merge`],
1902        // [`apply`], [`merge_status`], [`merge_as`] partition the
1903        // (Patch-strategy × PatchParams-posture × wire-endpoint) matrix
1904        // the workspace's wire-side patch family stamps. This pin
1905        // witnesses that [`merge_as`] stamps EXACTLY the
1906        // (Patch::Merge × apply_patch_params × primary-resource)
1907        // corner — distinct from [`merge`]'s
1908        // (Patch::Merge × PatchParams::default × primary-resource)
1909        // corner and from [`apply`]'s
1910        // (Patch::Apply × apply_patch_params × primary-resource)
1911        // corner. A regression that collapsed any two corners onto
1912        // ONE primitive (e.g. `merge_as` accidentally routing through
1913        // `apply`'s `Patch::Apply` posture, or reverting to
1914        // `PatchParams::default()` and drifting into `merge`'s corner)
1915        // would break the partition and surface HERE rather than as
1916        // silent field-manager attribution loss or SSA-vs-merge
1917        // semantics drift at the two pool-reconciler callsites.
1918
1919        // Corner witness: named-merge params ≠ default params
1920        let named = apply_patch_params("mgr");
1921        let default = PatchParams::default();
1922        assert_ne!(
1923            named.field_manager, default.field_manager,
1924            "merge_as's params carry a field manager; merge's do not — the corner distinction is load-bearing"
1925        );
1926        assert_ne!(
1927            named.force, default.force,
1928            "merge_as's params stamp force = true; merge's do not — the corner distinction is load-bearing"
1929        );
1930
1931        // Corner witness: merge strategy ≠ apply strategy at the same params
1932        let body = json!({"metadata": {"annotations": {"k": "v"}}});
1933        let merge_patch: Patch<&serde_json::Value> = Patch::Merge(&body);
1934        let apply_patch: Patch<&serde_json::Value> = Patch::Apply(&body);
1935        assert!(
1936            matches!(merge_patch, Patch::Merge(_)),
1937            "merge_as dispatches Patch::Merge, distinguishing it from apply's Patch::Apply corner"
1938        );
1939        assert!(
1940            matches!(apply_patch, Patch::Apply(_)),
1941            "apply dispatches Patch::Apply, distinguishing it from merge_as's Patch::Merge corner"
1942        );
1943    }
1944
1945    // ─── to_value_or_null substrate pins ────────────────────────────
1946    //
1947    // Bind [`to_value_or_null`] at fail-before-pass-after granularity
1948    // so a regression that swapped the serialiser
1949    // (`serde_json::to_string` for `to_value`), flipped the fallback
1950    // constant (`Value::Bool(false)` for `Value::Null`), narrowed the
1951    // `T: Serialize` bound (a `&str`-only monomorphisation), or
1952    // reshaped the return form (a `Result<Value, _>` in place of the
1953    // folded `Value`) surfaces HERE rather than as silent JSON-slot
1954    // drift at the two consumer sites (`annotation_body`'s
1955    // `annotations.<key>` leaf and `tatara_reconciler::patch::
1956    // phase_status_with`'s caller-named third slot).
1957
1958    #[test]
1959    fn to_value_or_null_folds_serializable_payload_into_the_corresponding_value_shape() {
1960        // Primary shape: each `T: Serialize` payload folds into the
1961        // exact `Value` shape `serde_json::to_value` yields for that
1962        // type. Sweep the representative payload shapes both consumer
1963        // sites pass in production:
1964        //
1965        //   - `Value::Null`  → signals::ingest strip via annotation_body
1966        //   - `String`       → transition_to_releasing stamp via annotation_body
1967        //   - `&'static str` → controller_allocation return-trigger via annotation_body
1968        //   - `&Identity`    → phase_status(phase, Some(&id)) via phase_status_with
1969        //   - `Vec<_>` ref   → Running-entry fluxResources via phase_status_with
1970        //   - `&Attestation` → Attested-entry attestation via phase_status_with
1971        //
1972        // Represented here by shape families the primitive must fold
1973        // (Null / owned-String / borrowed-str / borrowed-struct-ref /
1974        // borrowed-Vec-ref / borrowed-map-ref) without kube-side types
1975        // this crate's `tatara-process` layer doesn't own.
1976        assert_eq!(
1977            to_value_or_null(serde_json::Value::Null),
1978            serde_json::Value::Null
1979        );
1980        assert_eq!(to_value_or_null(String::from("Running")), json!("Running"));
1981        assert_eq!(to_value_or_null("true"), json!("true"));
1982
1983        #[derive(Serialize)]
1984        struct IdLike<'a> {
1985            name: &'a str,
1986            content_hash: &'a str,
1987        }
1988        let id_like = IdLike {
1989            name: "observability-stack",
1990            content_hash: "abc123",
1991        };
1992        assert_eq!(
1993            to_value_or_null(&id_like),
1994            json!({ "name": "observability-stack", "content_hash": "abc123" }),
1995            "borrowed-struct-ref folds through serde's rename_all-off default (snake_case field names verbatim)",
1996        );
1997
1998        let vec_like = vec!["a".to_string(), "b".to_string()];
1999        assert_eq!(to_value_or_null(&vec_like), json!(["a", "b"]));
2000
2001        let mut map_like = std::collections::BTreeMap::new();
2002        map_like.insert("k1", 1_u32);
2003        map_like.insert("k2", 2_u32);
2004        assert_eq!(to_value_or_null(&map_like), json!({ "k1": 1, "k2": 2 }));
2005    }
2006
2007    #[test]
2008    fn to_value_or_null_null_payload_round_trips_verbatim() {
2009        // The signals::ingest strip arm passes `serde_json::Value::
2010        // Null` verbatim so JSON merge patch interprets the resulting
2011        // annotation-body leaf as "remove key". A regression that
2012        // promoted the Null through a `String` wrap (silently producing
2013        // `Value::String("null")`) would break that strip semantics —
2014        // pin the round-trip at the primitive.
2015        let folded = to_value_or_null(serde_json::Value::Null);
2016        assert!(
2017            folded.is_null(),
2018            "Null payload folds to Value::Null, not to a JSON string \"null\"",
2019        );
2020    }
2021
2022    #[test]
2023    fn to_value_or_null_matches_pre_lift_hand_authored_chain_bytewise() {
2024        // Byte-shape parity witness against the pre-lift
2025        // `serde_json::to_value(<T>).unwrap_or(Value::Null)` chain both
2026        // consumer sites restated verbatim. Sweep representative
2027        // payload shapes; a regression that reshaped either link would
2028        // surface HERE rather than as silent JSON-slot drift at
2029        // `annotation_body` or `phase_status_with`.
2030        #[derive(Serialize)]
2031        struct Pair {
2032            phase: &'static str,
2033            since: &'static str,
2034        }
2035        let pair = Pair {
2036            phase: "Attested",
2037            since: "2026-09-05T00:00:00Z",
2038        };
2039        let via_primitive = to_value_or_null(&pair);
2040        let hand_authored = serde_json::to_value(&pair).unwrap_or(serde_json::Value::Null);
2041        assert_eq!(
2042            via_primitive, hand_authored,
2043            "to_value_or_null must byte-match the pre-lift `to_value(&pair).unwrap_or(Value::Null)` chain",
2044        );
2045
2046        for scalar in ["short", ""] {
2047            let via_primitive = to_value_or_null(scalar);
2048            let hand_authored = serde_json::to_value(scalar).unwrap_or(serde_json::Value::Null);
2049            assert_eq!(
2050                via_primitive, hand_authored,
2051                "scalar `&str` payload `{scalar}` must byte-match pre-lift chain",
2052            );
2053        }
2054
2055        let via_primitive = to_value_or_null(serde_json::Value::Null);
2056        let hand_authored =
2057            serde_json::to_value(serde_json::Value::Null).unwrap_or(serde_json::Value::Null);
2058        assert_eq!(
2059            via_primitive, hand_authored,
2060            "Null payload must byte-match pre-lift chain",
2061        );
2062    }
2063
2064    #[test]
2065    fn to_value_or_null_composes_at_annotation_body_and_phase_status_with_shape() {
2066        // Consumer-composition witness: [`annotation_body`] and (in the
2067        // reconciler crate) `phase_status_with` both fold their `T:
2068        // Serialize` extras through THIS primitive. Verify the
2069        // `annotation_body` side composes correctly at the exact leaf
2070        // slot; the reconciler-side consumer is exercised by that
2071        // crate's own `phase_status_with` pins that already sweep the
2072        // Serialize matrix. A regression that split the fold discipline
2073        // (a per-callsite drift in how the residual `Err` arm is
2074        // handled) would surface HERE at the composition-parity pin.
2075        for value_shape in [
2076            serde_json::Value::Null,
2077            json!("stamped"),
2078            json!(42),
2079            json!({ "nested": "shape" }),
2080        ] {
2081            let via_composition = annotation_body("k.io/v", value_shape.clone());
2082            let expected_leaf = to_value_or_null(value_shape);
2083            assert_eq!(
2084                via_composition["metadata"]["annotations"]["k.io/v"], expected_leaf,
2085                "annotation_body's leaf value equals to_value_or_null of the same payload",
2086            );
2087        }
2088    }
2089}