Skip to main content

tatara_process/
status.rs

1//! `ProcessStatus` sub-structures — conditions, checked boundaries, Flux refs.
2
3use chrono::{DateTime, Utc};
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8use crate::boundary::Condition;
9use crate::condition_type::ProcessConditionType;
10use crate::crd::Process;
11use crate::json_object::ValueGetExt;
12use crate::k8s_condition::K8sConditionStatus;
13
14/// Standard K8s Condition (shape of `metav1.Condition`).
15#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
16#[serde(rename_all = "camelCase")]
17pub struct ProcessCondition {
18    #[serde(rename = "type")]
19    pub type_: String,
20    pub status: String,
21    pub last_transition_time: DateTime<Utc>,
22    #[serde(default, skip_serializing_if = "Option::is_none")]
23    pub reason: Option<String>,
24    #[serde(default, skip_serializing_if = "Option::is_none")]
25    pub message: Option<String>,
26}
27
28impl ProcessCondition {
29    /// Compose a [`ProcessCondition`] stamped at "observed now" from a
30    /// typed [`ProcessConditionType`] + [`K8sConditionStatus`] pair,
31    /// a reason slug, and an optional message.
32    ///
33    /// Owns the 5-slot wire-shape backbone
34    ///
35    /// ```text
36    /// Self {
37    ///     type_:                <ConditionType>.as_wire_str().into(),
38    ///     status:               <StatusEnum>.as_wire_str().into(),
39    ///     last_transition_time: Utc::now(),
40    ///     reason:               Some(<reason>.into()),
41    ///     message:              <opt>,
42    /// }
43    /// ```
44    ///
45    /// that every sibling constructor on this impl block ([`Self::ready`],
46    /// [`Self::not_ready`], [`Self::attested`]) hand-authored pre-lift.
47    ///
48    /// Pre-lift the SAME 5-slot struct-literal skeleton recurred at
49    /// THREE sibling constructor bodies past the ★★ PRIME-DIRECTIVE
50    /// ≥ 2 duplication threshold, each restating (a) the typed-enum →
51    /// wire-string projection at the `type_` slot, (b) the peer typed-
52    /// enum → wire-string projection at the `status` slot, (c) the
53    /// `Utc::now()` clock stamp at the `last_transition_time` slot,
54    /// (d) the `Some(<reason>.into())` wrap at the `reason` slot. Post-
55    /// lift the three siblings name their two typed variants + reason +
56    /// message opt ONCE and route through this ONE composer; the shared
57    /// backbone lives at ONE substrate site so a future normalization
58    /// of the K8s Condition wire shape (adding `observed_generation:
59    /// Option<i64>` to match `metav1.Condition` v2, injecting a
60    /// `Clock` for deterministic tests replacing the direct
61    /// `Utc::now()` call, adding a `severity:` slot for K8s-Condition-
62    /// v3 style diagnostics) lands at ONE composer body and all three
63    /// sibling constructors — plus every future `ProcessCondition::*`
64    /// variant on this impl block — inherit the upgrade mechanically.
65    ///
66    /// The typed enum inputs bind the wire-form projection axis
67    /// structurally: a regression that swapped `K8sConditionStatus::
68    /// True` for `False` at ONE sibling (a mechanical copy-paste error
69    /// the pre-lift hand-authored `status: "True".into()` /
70    /// `status: "False".into()` string-literal restatements would not
71    /// have caught at compile time) is caught by the enum's closed
72    /// set, and the wire-string projection itself rides through the
73    /// SAME `as_wire_str()` projection every reader/writer pair in the
74    /// workspace already routes through — so a future spelling change
75    /// at ONE of those enums propagates mechanically to all three
76    /// sibling constructors without a per-site edit.
77    ///
78    /// Sibling to the pre-existing `K8sConditionStatus` /
79    /// `ProcessConditionType` closed-set variants on the (typed
80    /// wire-form × K8s-Condition-slot) axis: those closed sets own
81    /// the type-variant enumeration + wire-form projection; this
82    /// composer owns the K8s-Condition struct-literal composition
83    /// that binds two typed variants onto the wire.
84    ///
85    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
86    /// the 5-slot struct-literal backbone recurred at 3 hand-authored
87    /// sibling constructor sites past the ★★ PRIME-DIRECTIVE ≥ 2
88    /// duplication trigger and is lifted onto the ONE substrate
89    /// composer here). THEORY.md §II.1 invariant 5 (composition
90    /// preserves proofs — the pin block below binds the composer at
91    /// fail-before-pass-after granularity, so a regression that drifted
92    /// any slot's default — `Utc::now()` swapped for a fixed-anchor
93    /// stamp, `reason: Some(<reason>.into())` narrowed to a bare
94    /// `<reason>.into()` opting out of the K8s-Condition-mandated
95    /// `Option`-wrap, the typed-enum → wire-string projection at the
96    /// `type_` or `status` slot bypassed by an inline literal that
97    /// silently drifts off the closed-set owner — surfaces at
98    /// `status::tests::new_at_now_*` rather than as silent operator-
99    /// facing skew across the three sibling constructor callsites).
100    ///
101    /// # Delegation to [`Self::new_at`]
102    ///
103    /// The 5-slot struct-literal body lives at the clock-injectable
104    /// substrate peer [`Self::new_at`]; this composer supplies
105    /// `Utc::now()` as the `at` slot and delegates. The wall-clock
106    /// projection lives at ONE substrate site, so a future clock swap
107    /// (a monotonic-clock cross-check, a per-fleet skew tolerance,
108    /// promotion to an injectable `Clock` trait) lands at the SINGLE
109    /// `Utc::now()` call on this delegation body rather than at every
110    /// hand-authored `last_transition_time: Utc::now()` stamp — no
111    /// per-sibling edit at [`Self::ready`], [`Self::not_ready`], or
112    /// [`Self::attested`], and no edit at any future
113    /// `ProcessCondition::*` sibling that inherits the composer.
114    fn new_at_now(
115        type_: ProcessConditionType,
116        status: K8sConditionStatus,
117        reason: impl Into<String>,
118        message: Option<String>,
119    ) -> Self {
120        Self::new_at(type_, status, reason, message, Utc::now())
121    }
122
123    /// Clock-injectable substrate peer of [`Self::new_at_now`] — the
124    /// ONE substrate owner of the 5-slot wire-shape backbone
125    ///
126    /// ```text
127    /// Self {
128    ///     type_:                <ConditionType>.as_wire_str().into(),
129    ///     status:               <StatusEnum>.as_wire_str().into(),
130    ///     last_transition_time: <at>,
131    ///     reason:               Some(<reason>.into()),
132    ///     message:              <opt>,
133    /// }
134    /// ```
135    ///
136    /// with the wall-clock stamp lifted out onto an explicit `at:
137    /// DateTime<Utc>` slot so a caller (typically a deterministic-
138    /// clock test on this module or a future controller-side callsite
139    /// that already carries a wall-clock anchor threaded through its
140    /// own decide-tick) supplies the anchor rather than reading it
141    /// from `Utc::now()` implicitly.
142    ///
143    /// # Why it exists
144    ///
145    /// [`Self::new_at_now`]'s pre-lift body read `Utc::now()` inline
146    /// at the composer body — a shape the module's own tests already
147    /// bracket with `let before = Utc::now(); ... let after = Utc::
148    /// now(); assert!(<c>.last_transition_time >= before && <c>.
149    /// last_transition_time <= after)` to pin the wall-clock projection
150    /// non-deterministically. That non-determinism is a proof shape
151    /// every peer clock-anchored composer family in this crate has
152    /// already collapsed onto a `_now` + `_at` peer pair — see
153    /// [`crate::pool::PoolStatus::observed`] (`at`-slot substrate) +
154    /// [`crate::pool::PoolStatus::observed_now`] (wall-clock-anchored
155    /// peer), [`crate::allocation::AllocationStatus::transition`] +
156    /// [`crate::allocation::AllocationStatus::transition_now`],
157    /// [`crate::lifetime_clock::evaluate`] +
158    /// [`crate::lifetime_clock::evaluate_now`] — all binding the
159    /// `Utc::now()` read at exactly ONE substrate site (the `_now`
160    /// peer's body) with the `_at` peer owning the pure struct-literal
161    /// composition. The docstring on [`Self::new_at_now`] itself
162    /// called out the trajectory (`"a promotion of the direct
163    /// Utc::now() call to an injectable Clock for deterministic tests"`)
164    /// as its next compounding step; this method opens exactly that
165    /// step for the `ProcessCondition` composer family, closing the
166    /// pattern uniformity across the four peer axes.
167    ///
168    /// # Invariants
169    ///
170    /// - **Same slots as [`Self::new_at_now`]:** the four non-clock
171    ///   slots (`type_`, `status`, `reason`, `message`) ride through
172    ///   the SAME projections — typed enum → wire string at `type_` +
173    ///   `status`, `Some(<reason>.into())` wrap at `reason`, verbatim
174    ///   `Option<String>` pass-through at `message`. The peer pair
175    ///   differs only at the `last_transition_time` slot's projection.
176    /// - **`at` slot verbatim:** the caller-supplied `DateTime<Utc>`
177    ///   binds `last_transition_time` bytewise, with no accidental
178    ///   `Utc::now()` clamp / `Duration::seconds(0)` rounding / offset
179    ///   normalization at the composer body. Pinned by
180    ///   [`tests::new_at_binds_at_slot_to_supplied_datetime_verbatim`].
181    /// - **Delegation invariant:** [`Self::new_at_now`] composes
182    ///   through this primitive with `Utc::now()` at the `at` slot —
183    ///   a regression that re-inlined the 5-slot literal at
184    ///   [`Self::new_at_now`] (bypassing this substrate) surfaces at
185    ///   [`tests::new_at_now_routes_through_new_at_with_utc_now_stamp_bytewise`]
186    ///   rather than as silent skew between the wall-clock-anchored
187    ///   peer and any future `new_at` consumer that pinned a fixed
188    ///   anchor.
189    ///
190    /// # `#[must_use]`
191    ///
192    /// Every consumer feeds the returned [`ProcessCondition`] into a
193    /// `ProcessStatus.conditions` slot or a peer status-patch call.
194    /// Dropping the return means the condition composed for no
195    /// observable reason — the attribute surfaces that as a warning
196    /// at every call site.
197    ///
198    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
199    /// the 5-slot struct-literal backbone that recurred at three
200    /// sibling constructors' pre-lift bodies is now the SINGLE
201    /// substrate site every clock-injected AND every wall-clock-
202    /// anchored composer routes through). THEORY.md §II.1 invariant 5
203    /// (composition preserves proofs — the pair `Self::new_at` +
204    /// `Self::new_at_now` mirrors the four peer clock-anchored
205    /// composer families across the crate, so the workspace's
206    /// `<K8sWireResource>Condition` / `<CRD>Status` composer family
207    /// stays uniform on the (clock-injectable, wall-clock-anchored)
208    /// peer axis; a future primitive on the same axis inherits the
209    /// convention structurally).
210    #[must_use]
211    fn new_at(
212        type_: ProcessConditionType,
213        status: K8sConditionStatus,
214        reason: impl Into<String>,
215        message: Option<String>,
216        at: DateTime<Utc>,
217    ) -> Self {
218        Self {
219            type_: type_.as_wire_str().into(),
220            status: status.as_wire_str().into(),
221            last_transition_time: at,
222            reason: Some(reason.into()),
223            message,
224        }
225    }
226
227    pub fn ready(reason: impl Into<String>, message: Option<String>) -> Self {
228        Self::new_at_now(
229            ProcessConditionType::Ready,
230            K8sConditionStatus::True,
231            reason,
232            message,
233        )
234    }
235
236    pub fn not_ready(reason: impl Into<String>, message: impl Into<String>) -> Self {
237        Self::new_at_now(
238            ProcessConditionType::Ready,
239            K8sConditionStatus::False,
240            reason,
241            Some(message.into()),
242        )
243    }
244
245    pub fn attested(root: &str) -> Self {
246        Self::new_at_now(
247            ProcessConditionType::Attested,
248            K8sConditionStatus::True,
249            "AttestationWritten",
250            Some(format!("composed_root={root}")),
251        )
252    }
253}
254
255/// Reference to a FluxCD resource emitted as part of this Process.
256#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
257#[serde(rename_all = "camelCase")]
258pub struct FluxResourceRef {
259    pub api_version: String,
260    pub kind: String,
261    pub name: String,
262    pub namespace: String,
263    #[serde(default)]
264    pub ready: bool,
265    #[serde(default, skip_serializing_if = "Option::is_none")]
266    pub message: Option<String>,
267    #[serde(default, skip_serializing_if = "Option::is_none")]
268    pub last_check: Option<DateTime<Utc>>,
269}
270
271impl FluxResourceRef {
272    /// Pure typed projection of the four fetch coordinates
273    /// `(namespace, api_version, kind, name)` every consumer that
274    /// dispatches this persisted reference through kube-rs's dynamic-
275    /// object surface splats by hand pre-lift. The 4-tuple binds the
276    /// slot order at ONE typed accessor so a copy-paste at any downstream
277    /// consumer cannot swap two adjacent `&str` slots in the fetch call.
278    ///
279    /// Peer projection to
280    /// [`crate::k8s_wire_identity::K8sWireIdentity`] on the static-
281    /// identity axis: [`K8sWireIdentity`] carries a
282    /// `(&'static str, &'static str)` closed-set variant's pair for
283    /// emit-time (RENDER phase) composition; this method carries the
284    /// full `(ns, apiVersion, kind, name)` 4-slot borrow for fetch-time
285    /// (VERIFY / ATTEST-heartbeat) composition where the ref's payload
286    /// comes back off the persisted `ProcessStatus.flux_resources`
287    /// slice with owned `String`s rather than static literals. The two
288    /// primitives partition the fetch axis by whether the caller starts
289    /// from a closed-set variant (emit-time) or a persisted status
290    /// slice (fetch-time).
291    ///
292    /// Pre-lift the 5-slot `ssapply::fetch(client, &r.namespace,
293    /// &r.api_version, &r.kind, &r.name)` splat was hand-authored at
294    /// TWO sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold
295    /// in `tatara-reconciler::phase_machine`:
296    /// * `handle_running` — the VERIFY-phase per-ref readiness probe
297    ///   that populates the updated `FluxResourceRef` slice with
298    ///   `ready` + `message` + `last_check`.
299    /// * `handle_attested` — the ATTEST-heartbeat drift detector that
300    ///   short-circuits on the first non-Ready ref.
301    ///
302    /// Both sites splatted the SAME four `&r.X` field borrows in the
303    /// SAME order into raw `ssapply::fetch`. A copy-paste that swapped
304    /// two adjacent `&str` slots (`&r.api_version` and `&r.kind` are
305    /// both strings that look interchangeable to a mechanical
306    /// substitution) would silently 404 at wire time and diagnose as a
307    /// broken CRD rather than as slot skew at the callsite. Post-lift
308    /// each site names the ref ONCE and unpacks it through this ONE
309    /// projection; the slot order binds structurally at the tuple
310    /// return so a caller cannot desync one axis.
311    ///
312    /// A future addition (a case-fold normalization on the group, a
313    /// virtual-cluster prefix rewrite for multi-tenancy, a
314    /// `generateName` fallback on the name slot, a cluster-cache
315    /// short-circuit inserted between the projection and the fetch
316    /// call) lands at this ONE method and every downstream fetch
317    /// consumer inherits the upgrade mechanically — no per-site edit
318    /// at `handle_running` / `handle_attested` / any future kenshi-
319    /// runner / mirror-audit / drift-probe consumer that grows a third
320    /// consumer.
321    ///
322    /// Return-order pin lives at
323    /// [`tests::flux_resource_ref_fetch_coords_binds_slots_by_position`]
324    /// so a regression that swapped `namespace` and `api_version`
325    /// (both `String`, same type) inside the tuple constructor fails-
326    /// loudly here rather than as a silent wire-time 404 at every
327    /// downstream fetch consumer.
328    ///
329    /// Theory grounding: THEORY.md §II.1 invariant 5 (composition
330    /// preserves proofs — the 4-tuple slot order binds at ONE typed
331    /// projection so a regression across the two fields of the same
332    /// `String` type fails at the projection's positional pin rather
333    /// than at every downstream fetch consumer). THEORY.md §VI.1
334    /// (generation over composition — the 5-slot splat recurred at
335    /// two hand-authored sites past the ≥ 2 duplication trigger, and
336    /// is lifted to ONE typed borrow-projection here).
337    pub fn fetch_coords(&self) -> (&str, &str, &str, &str) {
338        (&self.namespace, &self.api_version, &self.kind, &self.name)
339    }
340
341    /// Compose a `FluxResourceRef` stamped at "observed now" — the
342    /// `last_check` slot is set to `Some(Utc::now())` at ONE substrate
343    /// owner, and the four coordinate slots + `ready` + `message`
344    /// are bound positionally so a slot-swap regression surfaces at
345    /// the constructor's positional pin rather than as silent drift
346    /// at every downstream `ProcessStatus.flux_resources` writer.
347    ///
348    /// Pre-lift the 7-slot `FluxResourceRef { …, last_check:
349    /// Some(chrono::Utc::now()) }` struct-literal was hand-authored
350    /// at TWO sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
351    /// threshold in `tatara-reconciler::phase_machine`:
352    /// * `handle_running` — the VERIFY-phase per-ref rebuild that
353    ///   restamps each polled ref with fresh `ready` + `message` +
354    ///   `last_check`.
355    /// * `flux_ref_from_json` — the post-SSA initial-state seeder
356    ///   that stamps a freshly-applied ref as `ready = false`,
357    ///   `message = Some("applied; awaiting reconciliation")`,
358    ///   `last_check = Some(Utc::now())`.
359    ///
360    /// Both sites restated the SAME seven field bindings in the
361    /// SAME order, and both restated the SAME `Some(chrono::Utc::
362    /// now())` stamp. A copy-paste that swapped two adjacent
363    /// `String` slots (`api_version` and `kind`, `kind` and `name`,
364    /// or `name` and `namespace` are all mechanically
365    /// indistinguishable at the type level) would silently persist
366    /// a slot-inverted ref that the downstream Flux fetch consumer
367    /// (via [`Self::fetch_coords`]) would then 404 on. Post-lift
368    /// both sites name the six inputs ONCE and route through this
369    /// ONE composer; the seventh slot (`last_check`) is stamped at
370    /// the composer's body so a future injection point (a fake
371    /// clock for testing, a monotonic-clock cross-check, a per-
372    /// fleet skew tolerance) lands at ONE substrate site rather
373    /// than at every hand-authored `Some(chrono::Utc::now())` stamp.
374    ///
375    /// Return-order pin lives at
376    /// [`tests::flux_resource_ref_observed_binds_slots_by_position`]
377    /// so a regression that swapped `api_version` and `kind` (both
378    /// `String`, same type) inside the constructor's argument list
379    /// fails-loudly here rather than as a silent wire-time 404 at
380    /// every downstream fetch consumer.
381    ///
382    /// Theory grounding: THEORY.md §II.1 invariant 5 (composition
383    /// preserves proofs — the 6-slot positional binding + the
384    /// `last_check` stamp compose at ONE typed owner, so a
385    /// regression across the four `String` coordinate slots fails
386    /// at the composer's positional pin rather than at every
387    /// downstream Flux status writer). THEORY.md §VI.1 (generation
388    /// over composition — the 7-slot struct-literal recurred at two
389    /// hand-authored sites past the ≥ 2 duplication trigger, and is
390    /// lifted to ONE typed composer here).
391    pub fn observed(
392        api_version: String,
393        kind: String,
394        name: String,
395        namespace: String,
396        ready: bool,
397        message: Option<String>,
398    ) -> Self {
399        Self {
400            api_version,
401            kind,
402            name,
403            namespace,
404            ready,
405            message,
406            last_check: Some(Utc::now()),
407        }
408    }
409
410    /// Compose a `FluxResourceRef` in the pre-observation shape — the
411    /// 4-slot coordinate binding with the three status slots defaulted
412    /// (`ready: false`, `message: None`, `last_check: None`). The
413    /// deterministic-fixture peer of [`Self::observed`] on the same
414    /// `→ FluxResourceRef` composer axis: `observed` reads the wall
415    /// clock and takes 6 args (a live post-fetch stamp), `pending`
416    /// reads no clock and takes 4 args (a pre-observation fixture
417    /// seed, and the natural base for `..base.clone()` spread updates
418    /// that vary a single slot for a per-corner test sweep).
419    ///
420    /// Pre-lift the SAME 7-slot `FluxResourceRef { api_version, kind,
421    /// name, namespace, ready: false, message: None, last_check: None
422    /// }` struct-literal was hand-authored at THREE workspace-wide
423    /// fixture sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
424    /// threshold:
425    ///
426    /// * [`crate::crd`]
427    ///   `crd::observed_flux_resources_tests::sample_flux_ref(name)`
428    ///   — the shared `Kustomization`/`flux-system` fixture the
429    ///   `Process::observed_flux_resources` pin family destructures
430    ///   for its `flux_resources`-populated corners.
431    /// * `tatara-reconciler::ssapply::tests::sample_flux_ref_for_diag`
432    ///   — the `HelmRelease`/`flux-system` fixture the
433    ///   `flux_ref_fetch_error_context` diagnostic-wording pin
434    ///   family destructures for its (kind, name) slot-coverage
435    ///   sweep.
436    /// * `tatara-reconciler::ssapply::tests::
437    ///   flux_ref_fetch_error_context_matches_pre_lift_hand_authored_wording`
438    ///   — the inline 7-slot literal inside the cross-substrate
439    ///   coherence pin's per-case sweep over three distinct
440    ///   `(api_version, kind, name, namespace)` tuples.
441    ///
442    /// All THREE sites restated the SAME seven field bindings in the
443    /// SAME order and the SAME three defaulted status slots (`ready:
444    /// false, message: None, last_check: None`), differing only in
445    /// the four coordinate `String` values. Post-lift each callsite
446    /// reads `FluxResourceRef::pending(<api_version>, <kind>, <name>,
447    /// <namespace>)` and the four-slot bind + three-slot default
448    /// sinks live at ONE substrate owner.
449    ///
450    /// The `impl Into<String>` signature accepts BOTH `&'static str`
451    /// (the fixture-helper sites that spell coordinate literals
452    /// inline) AND owned `String` (a future callsite handing off a
453    /// dynamically-derived coordinate) without widening. Matches the
454    /// discipline of the sibling substrate composers
455    /// [`crate::pool::PoolMember::unallocated`] +
456    /// [`crate::allocation::AllocationRef::new`] on the identity-slot
457    /// axis.
458    ///
459    /// A future normalization (a case-fold on the group, a
460    /// virtual-cluster prefix rewrite for multi-tenancy, a stricter
461    /// kind gate, a `generateName` fallback on the name slot, a
462    /// canonical rename of one of the three defaulted status slots
463    /// to a typed `PreObservation` marker) lands at THIS ONE
464    /// substrate primitive and every downstream fixture / helper
465    /// inherits the upgrade mechanically — no per-site edit at any
466    /// of the THREE listed callers or at future consumers (a
467    /// stable-name claim-arbiter's pending-ref seed, a kenshi-runner
468    /// pre-observation fixture, a mirror-audit drift-probe test
469    /// helper).
470    ///
471    /// Theory grounding: THEORY.md §II.1 invariant 5 (composition
472    /// preserves proofs — the 4-slot positional binding + the three
473    /// defaulted status slots compose at ONE typed owner, so a
474    /// regression across the four `String` coordinate slots fails at
475    /// the composer's positional pin rather than at every downstream
476    /// fixture consumer). THEORY.md §VI.1 (generation over
477    /// composition — the 7-slot struct-literal recurred at three
478    /// hand-authored fixture sites past the ≥ 2 duplication trigger,
479    /// and is lifted to ONE typed composer here).
480    #[must_use]
481    pub fn pending(
482        api_version: impl Into<String>,
483        kind: impl Into<String>,
484        name: impl Into<String>,
485        namespace: impl Into<String>,
486    ) -> Self {
487        Self {
488            api_version: api_version.into(),
489            kind: kind.into(),
490            name: name.into(),
491            namespace: namespace.into(),
492            ready: false,
493            message: None,
494            last_check: None,
495        }
496    }
497}
498
499/// Identifying coordinates of a rendered K8s resource — the
500/// `(apiVersion, kind, metadata.name, metadata.namespace)` 4-tuple
501/// every consumer that walks a rendered `serde_json::Value` resource
502/// unwraps by hand pre-lift.
503///
504/// The three K8s API-path segments (`apiVersion`, `kind`,
505/// `metadata.name`) are REQUIRED — a rendered resource missing any
506/// of them cannot be applied via kube-rs's dynamic API surface, so
507/// the extraction fails fast at the boundary rather than as a
508/// downstream `Api::patch` panic. `metadata.namespace` is
509/// intentionally kept as `Option<String>` because different consumers
510/// resolve the fallback differently: `apply_owned` uses the
511/// caller-supplied `namespace: &str` argument (the reconciler already
512/// resolved the target namespace upstream), while `flux_ref_from_json`
513/// records the K8s canonical `"default"` fallback into the persisted
514/// `FluxResourceRef.namespace` slot. The peer method
515/// [`Self::namespace_or_default`] applies the K8s canonical fallback
516/// (`Process::DEFAULT_NAMESPACE = "default"`) for consumers wanting
517/// the same shape [`FluxResourceRef.namespace`] carries.
518///
519/// Pre-lift the 3+1 slot extraction was hand-authored at TWO sites
520/// past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold across
521/// `tatara-reconciler`:
522/// * `tatara-reconciler::phase_machine::flux_ref_from_json` — the
523///   post-SSA `FluxResourceRef` builder that persists into
524///   `ProcessStatus.flux_resources`; namespace half fallback-
525///   defaulted to `"default"`.
526/// * `tatara-reconciler::ssapply::apply_owned` — the SSA entry
527///   point that extracts (apiVersion, kind, name) for the
528///   [`kube::Api::patch`] call; namespace half discarded (the
529///   `namespace: &str` argument comes from the caller upstream).
530///
531/// Both callsites restated the same three
532/// `.get(K).and_then(|v| v.as_str()).ok_or_else(|| anyhow!(...))?
533/// .to_string()` incantations with subtly different error wording
534/// (`"resource missing X"` vs `"rendered resource missing X"`); post-
535/// lift both route through this ONE substrate owner with the
536/// canonical `"rendered resource missing X"` wording. A future
537/// addition (case-fold on the group, a rename of the namespace
538/// fallback, a stricter kind gate, a Unicode-safe collation step,
539/// support for `metadata.generateName` as a name fallback) lands at
540/// the primitive's body on the substrate, not at 2 independent
541/// hand-writes across 2 reconciler files.
542///
543/// Namespace fallback const is shared with
544/// [`Process::DEFAULT_NAMESPACE`] — a rename of the K8s canonical
545/// default namespace lands at that ONE workspace-wide const, not at
546/// per-primitive local literals that would drift silently.
547#[derive(Clone, Debug, PartialEq, Eq)]
548pub struct RenderedResourceCoords {
549    /// `apiVersion` — the group+version pair kube-rs uses to resolve
550    /// the `ApiResource` for the SSA call.
551    pub api_version: String,
552    /// `kind` — the resource kind (Kustomization, HelmRelease, …).
553    pub kind: String,
554    /// `metadata.name` — the API-path leaf segment.
555    pub name: String,
556    /// `metadata.namespace` — raw from the resource, `None` when the
557    /// slot is absent (a cluster-scoped resource, or a namespaced
558    /// resource whose namespace was left for the API server to
559    /// substitute). Consumers apply their own fallback:
560    /// [`Self::namespace_or_default`] applies the K8s canonical
561    /// `"default"` (matching what [`FluxResourceRef.namespace`]
562    /// records); other consumers substitute a caller-supplied string
563    /// (see `tatara-reconciler::ssapply::apply_owned`).
564    pub namespace: Option<String>,
565}
566
567impl RenderedResourceCoords {
568    /// Extract the 4-tuple from a rendered K8s resource JSON `Value`.
569    ///
570    /// Fails with a canonical `"rendered resource missing X"` message
571    /// when any of the three required slots (`apiVersion`, `kind`,
572    /// `metadata.name`) is absent or non-string; `metadata.namespace`
573    /// is optional and captured as `None` when absent.
574    ///
575    /// The error wording is pinned by
576    /// [`tests::rendered_resource_coords_error_wording_is_canonical`]
577    /// so a regression that reshaped the message surfaces at the test
578    /// surface rather than as silent drift between the two pre-lift
579    /// call sites (which used subtly different wording — `"resource
580    /// missing X"` in `apply_owned` vs `"rendered resource missing
581    /// X"` in `flux_ref_from_json`).
582    pub fn from_json(res: &Value) -> anyhow::Result<Self> {
583        // The three REQUIRED-slot extracts (`apiVersion`, `kind`,
584        // `metadata.name`) route through the ONE substrate primitive
585        // `Self::required_str` — the required-extract sibling of
586        // `crate::json_object::ValueGetExt::get_str` on the same
587        // rendered-resource axis. A future normalization (Unicode
588        // NFC-fold, whitespace trim, empty-string rejection) lands
589        // at the primitive body and every downstream consumer of the
590        // canonical `"rendered resource missing X"` wire form inherits
591        // it mechanically. The optional `metadata.namespace` slot
592        // routes directly through the ONE substrate primitive
593        // `crate::json_object::ValueGetExt::get_str` on the
594        // `Option<&Value>` receiver arm — the outer-optionality
595        // widening added alongside the `Value` + `Map<String, Value>`
596        // impls. Pre-lift this callsite hand-authored the outer-
597        // optionality closure `metadata.and_then(|m| m.get_str
598        // ("namespace"))` past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
599        // threshold (this site + `Self::required_str`'s body); post-
600        // lift the closure disappears and the axis-family method-call
601        // surface stays identical to the `Value` / `Map` receiver
602        // callers.
603        let api_version = Self::required_str(Some(res), "apiVersion", "apiVersion")?;
604        let kind = Self::required_str(Some(res), "kind", "kind")?;
605        let metadata = res.get("metadata");
606        let name = Self::required_str(metadata, "name", "metadata.name")?;
607        let namespace = metadata.get_str("namespace").map(str::to_string);
608        Ok(Self {
609            api_version,
610            kind,
611            name,
612            namespace,
613        })
614    }
615
616    /// Diagnostic prefix stamped ahead of every required-slot label in
617    /// the canonical error wire form. Owned in ONE workspace-wide place
618    /// so a rename (a fleet-wide switch to `"resource is missing"` /
619    /// `"missing rendered-resource field"`) lands here and every
620    /// downstream `.to_string()`-consumer + operator-facing log grep
621    /// inherits the rename mechanically, not at 3 hand-authored
622    /// `anyhow!(…)` restatements.
623    pub const MISSING_MESSAGE_PREFIX: &'static str = "rendered resource missing";
624
625    /// Required-slot extract on a rendered-resource JSON `Value` — the
626    /// substrate owner of the paired `.get_str(<key>).ok_or_else(||
627    /// anyhow!("rendered resource missing <slot>"))?.to_string()`
628    /// four-link chain every REQUIRED slot on a rendered `Value`
629    /// walks pre-lift.
630    ///
631    /// The primitive accepts an `Option<&Value>` receiver so BOTH
632    /// shallow reads (top-level `apiVersion` / `kind` on the resource
633    /// root, callers thread `Some(res)`) AND one-level-nested reads
634    /// (`metadata.name` walking through `res.get("metadata")`,
635    /// callers thread the `Option<&Value>` handle the `.get()` step
636    /// returns) reach the same owner. The `key` slot is the wire-form
637    /// name the underlying [`ValueGetExt::get_str`] looks up on the
638    /// object; the `error_slot` slot is the diagnostic label stamped
639    /// into the error's `Display` output. The two are decoupled so
640    /// `metadata.name` can look up `"name"` on the `metadata` sub-
641    /// object while reporting the dotted `"metadata.name"` path an
642    /// operator bisecting a fault sees in the log.
643    ///
644    /// Ok arm returns `String` (owned) rather than the borrowed
645    /// `&str` [`ValueGetExt::get_str`] returns — every downstream
646    /// slot on the [`RenderedResourceCoords`] struct is an owned
647    /// `String`, so the primitive absorbs the `str::to_string`
648    /// coerce that pre-lift lived at three hand-authored callsites.
649    /// Err arm carries an `anyhow::Error` whose `Display` reads
650    /// exactly `"<Self::MISSING_MESSAGE_PREFIX> <error_slot>"` —
651    /// byte-identical to the pre-lift hand-authored `anyhow!(
652    /// "rendered resource missing {slot}")` wire form.
653    ///
654    /// ### Fires on all four absent-shape corners
655    ///
656    /// The primitive returns `Err` on ALL four ways a required
657    /// slot can miss:
658    ///
659    /// 1. Receiver is `None` — the `metadata.name` corner when the
660    ///    top-level `metadata` object itself is absent (the caller
661    ///    threaded `res.get("metadata")` which returned `None`).
662    /// 2. Slot is absent — the receiver is present but does not
663    ///    carry a value at `key`.
664    /// 3. Slot is present but non-string — a fixture bug that
665    ///    stamped a JSON number / bool / object / array at the
666    ///    slot; the `get_str` step falls through and the primitive
667    ///    reports the slot as missing (matching the pre-lift
668    ///    behavior where every non-string variant surfaced as the
669    ///    same `"missing"` diagnostic — pinning "cannot be applied
670    ///    via kube-rs's dynamic API surface" as the shared
671    ///    failure mode).
672    /// 4. Receiver is non-object — a resource authored as a JSON
673    ///    array / string / null at any of the levels the primitive
674    ///    walks (the `get_str` step returns `None` verbatim).
675    ///
676    /// All four corners produce the SAME wire form so an operator's
677    /// `rg "rendered resource missing"` sweep hits exactly one
678    /// footprint per faulted slot, not four differently-worded
679    /// diagnostics per absent-shape variant.
680    ///
681    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
682    /// the 4-link `.get_str(<key>).ok_or_else(|| anyhow!("rendered
683    /// resource missing <slot>"))?.to_string()` shape recurred at 3
684    /// hand-authored sites past the ★★ PRIME-DIRECTIVE ≥ 2
685    /// duplication trigger, and is lifted to ONE substrate owner
686    /// here). THEORY.md §II.1 invariant 5 (composition preserves
687    /// proofs — a regression that drifted the diagnostic prefix
688    /// wording at ONE site would silently pass the two sibling
689    /// pins and fail HERE; post-lift the wire form is owned once
690    /// at [`Self::MISSING_MESSAGE_PREFIX`] and every downstream
691    /// composition inherits the rename mechanically).
692    fn required_str(
693        v: Option<&Value>,
694        key: &'static str,
695        error_slot: &'static str,
696    ) -> anyhow::Result<String> {
697        // The `Option<&Value>` outer-optionality unwrap-then-project
698        // step rides through the substrate primitive
699        // `crate::json_object::ValueGetExt::get_str` on the
700        // `Option<&Value>` receiver arm — the outer-optionality
701        // widening sibling of the `Value` / `Map<String, Value>`
702        // impls. Pre-lift this body hand-authored the outer-
703        // optionality closure `v.and_then(|x| x.get_str(key))` past
704        // the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold (this
705        // helper's body + the sibling `metadata.namespace` walk in
706        // `Self::from_json`); post-lift the closure disappears and
707        // the axis-family method-call surface stays identical to the
708        // `Value` / `Map` receiver callers.
709        v.get_str(key).map(str::to_string).ok_or_else(|| {
710            anyhow::anyhow!(
711                "{prefix} {slot}",
712                prefix = Self::MISSING_MESSAGE_PREFIX,
713                slot = error_slot,
714            )
715        })
716    }
717
718    /// `metadata.namespace` slice with the K8s canonical `"default"`
719    /// fallback applied — matching what [`Process::DEFAULT_NAMESPACE`]
720    /// spells for the `Process`-borne coordinate primitive family
721    /// and what [`FluxResourceRef.namespace`] records into
722    /// `ProcessStatus.flux_resources`.
723    pub fn namespace_or_default(&self) -> &str {
724        self.namespace
725            .as_deref()
726            .unwrap_or(Process::DEFAULT_NAMESPACE)
727    }
728}
729
730/// A boundary condition paired with its current satisfaction state.
731#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
732#[serde(rename_all = "camelCase")]
733pub struct CheckedCondition {
734    #[serde(flatten)]
735    pub condition: Condition,
736    pub satisfied: bool,
737    #[serde(default, skip_serializing_if = "Option::is_none")]
738    pub last_check: Option<DateTime<Utc>>,
739    #[serde(default, skip_serializing_if = "Option::is_none")]
740    pub message: Option<String>,
741}
742
743impl CheckedCondition {
744    /// True iff every [`CheckedCondition`] in the slice has
745    /// `satisfied == true` — the ONE-line collapse of the paired
746    /// `checked.iter().all(|c| c.satisfied)` incantation the
747    /// reconciler's precondition + postcondition boundary gates both
748    /// spelled by hand pre-lift.
749    ///
750    /// Pre-lift the SAME `.iter().all(|c| c.satisfied)` chain was
751    /// hand-authored at TWO sites past the ★★ PRIME-DIRECTIVE ≥ 2
752    /// duplication threshold in `tatara-reconciler::phase_machine`,
753    /// each walking the SAME `Vec<CheckedCondition>` → `bool`
754    /// projection to gate a phase transition on a boundary predicate:
755    /// * `handle_execing` — the PROVE-phase precondition gate that
756    ///   stays in Execing (heartbeat requeue) while any precondition
757    ///   remains unsatisfied and proceeds to RENDER only when every
758    ///   precondition holds.
759    /// * `handle_running` — the VERIFY-phase postcondition gate that
760    ///   stays in Running (heartbeat requeue) while any postcondition
761    ///   remains unsatisfied and advances to Attested only when every
762    ///   postcondition holds.
763    ///
764    /// Both sites walked the SAME `Iterator::all` short-circuit on the
765    /// SAME `bool` slot of the SAME struct. Post-lift both consumers
766    /// name the slice ONCE and route through this ONE primitive; the
767    /// vacuous-truth corner (empty slice → `true`, matching
768    /// [`Iterator::all`]'s empty-input identity) sits at ONE substrate
769    /// site so a future normalization (a per-slot weight overlay, a
770    /// per-kind override that treats `Warn`-severity failures as
771    /// satisfied, a compliance-baseline gate that requires N-of-M
772    /// rather than all-of-M) lands at ONE substrate function and both
773    /// downstream phase gates inherit the upgrade mechanically.
774    ///
775    /// Return-form axis: `bool` — the exact type each phase gate
776    /// pre-lift bound at `let all_pass = <chain>;` and immediately
777    /// consumed in a `!all_pass` short-circuit + a `message` slot's
778    /// ternary branch. The `&[Self]` argument accepts every pre-lift
779    /// slice provenance verbatim: a `&Vec<CheckedCondition>` (both
780    /// pre-lift sites had the `Vec` on the stack from
781    /// [`crate::phase_machine::evaluate_conditions`]'s owned return)
782    /// coerces through auto-deref, so no callsite has to change its
783    /// upstream provenance to route through the primitive.
784    ///
785    /// Peer to the sibling projection [`Self::satisfied`] on the (row
786    /// scope × predicate) axis pair: `satisfied` is the per-row
787    /// projection; `all_satisfied` is the slice-wide fold of the same
788    /// bit. Both live on `CheckedCondition` so a future rename or
789    /// per-slot normalization travels through the same owner without
790    /// splitting between "per-row" and "slice-wide" call sinks.
791    ///
792    /// Return-shape pin lives at
793    /// [`tests::checked_condition_all_satisfied_matches_pre_lift_iter_all_chain_shape`]
794    /// so a regression that flipped the fold direction (`any` for
795    /// `all`), inverted the bit (`!c.satisfied`), or reshaped the
796    /// return form (an owned `Vec<bool>` instead of the folded `bool`)
797    /// fails-loudly here rather than as silent operator-facing skew
798    /// between the pre-lift `if !all_pass { requeue }` gate and the
799    /// post-lift call — every downstream consumer would still
800    /// short-circuit but on inverted semantics.
801    ///
802    /// Theory grounding: THEORY.md §VI.1 (generation over composition
803    /// — the 1-line `.iter().all(...)` chain recurred at two hand-
804    /// authored sites past the ≥ 2 duplication trigger, and is lifted
805    /// to ONE typed fold here). THEORY.md §II.1 invariant 5
806    /// (composition preserves proofs — the empty-slice vacuous-truth
807    /// corner + the fold direction + the projected bit's polarity all
808    /// bind at ONE substrate site, so a regression across any of the
809    /// three surfaces at [`tests::checked_condition_all_satisfied_*`]
810    /// pin rather than as silent gate-flip at every downstream phase
811    /// handler).
812    #[must_use]
813    pub fn all_satisfied(checked: &[Self]) -> bool {
814        checked.iter().all(|c| c.satisfied)
815    }
816}
817
818/// Summary of boundary verification.
819#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
820#[serde(rename_all = "camelCase")]
821pub struct BoundaryStatus {
822    #[serde(default)]
823    pub preconditions: Vec<CheckedCondition>,
824    #[serde(default)]
825    pub postconditions: Vec<CheckedCondition>,
826    /// Absolute deadline for VERIFY (derived from `spec.boundary.timeout`).
827    #[serde(default, skip_serializing_if = "Option::is_none")]
828    pub deadline: Option<DateTime<Utc>>,
829}
830
831/// Summary of compliance checks at the latest attestation.
832#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
833#[serde(rename_all = "camelCase")]
834pub struct ComplianceStatus {
835    #[serde(default, skip_serializing_if = "Option::is_none")]
836    pub baseline: Option<String>,
837    pub satisfied: u32,
838    pub violated: u32,
839    pub total: u32,
840    #[serde(default)]
841    pub violations: Vec<String>,
842}
843
844#[cfg(test)]
845mod tests {
846    use super::*;
847    use serde_json::json;
848
849    // ─── ProcessCondition writer / K8sConditionStatus substrate pins ─
850    //
851    // Byte-shape parity pins between `ProcessCondition::{ready,
852    // not_ready, attested}` writer output and the pre-lift hand-
853    // authored `"True"` / `"False"` `status` slot literals every
854    // downstream K8s API server + K8s-Condition-reading peer depends
855    // on. Post-lift the writers compose through
856    // `K8sConditionStatus::<V>.as_wire_str()`; these pins catch a
857    // regression at the substrate primitive (a lower-case drift, a
858    // whitespace prefix, a `serde(rename)` addition on the enum)
859    // that would silently reshape every emitted Process
860    // `status.conditions[]` slot away from the K8s wire form.
861
862    /// Fail-before-pass-after: `ProcessCondition::ready` emits the
863    /// exact-case ASCII `"True"` on the `status` slot the K8s API
864    /// server accepts, byte-identical to the pre-lift hand-authored
865    /// `status: "True".into()` literal. A regression at the substrate
866    /// primitive (a `to_lowercase` pass, a case-drifted variant
867    /// literal in `k8s_condition::K8sConditionStatus::as_wire_str`)
868    /// surfaces HERE, not as silent operator-facing wire-form skew
869    /// on the emitted Process CRD.
870    #[test]
871    fn ready_writer_status_slot_matches_pre_lift_true_literal_bytewise() {
872        let c = ProcessCondition::ready("ObservedRunning", Some("healthy".into()));
873        assert_eq!(c.type_, "Ready");
874        assert_eq!(c.status, "True");
875    }
876
877    /// Fail-before-pass-after: `ProcessCondition::not_ready` emits
878    /// the exact-case ASCII `"False"` on the `status` slot, byte-
879    /// identical to the pre-lift hand-authored `status: "False".
880    /// into()` literal.
881    #[test]
882    fn not_ready_writer_status_slot_matches_pre_lift_false_literal_bytewise() {
883        let c = ProcessCondition::not_ready("ObservedFailed", "boom");
884        assert_eq!(c.type_, "Ready");
885        assert_eq!(c.status, "False");
886    }
887
888    /// Fail-before-pass-after: `ProcessCondition::attested` emits
889    /// the exact-case ASCII `"True"` on the `status` slot with the
890    /// `"Attested"` type row, byte-identical to the pre-lift hand-
891    /// authored `type_: "Attested".into()` + `status: "True".into()`
892    /// pair.
893    #[test]
894    fn attested_writer_status_slot_matches_pre_lift_true_literal_bytewise() {
895        let c = ProcessCondition::attested("blake3:abc123");
896        assert_eq!(c.type_, "Attested");
897        assert_eq!(c.status, "True");
898        // Message body preserves the composed_root diagnostic
899        // wording verbatim — the substrate lift only reshaped the
900        // `status` slot, not the human-facing message.
901        assert_eq!(c.message.as_deref(), Some("composed_root=blake3:abc123"));
902        assert_eq!(c.reason.as_deref(), Some("AttestationWritten"));
903    }
904
905    /// Writer/reader wire-form parity: the `status` slot every
906    /// writer here emits is the SAME byte-shape the
907    /// `K8sConditionStatus::from_wire_str` reader in
908    /// `tatara-reconciler::ssapply::ready_condition_value` accepts.
909    /// A regression that drifted `as_wire_str` at ONE variant would
910    /// silently desynchronize every writer/reader pair in the
911    /// workspace; this pin surfaces the drift at the substrate.
912    #[test]
913    fn writer_output_round_trips_through_from_wire_str() {
914        let ready = ProcessCondition::ready("R", None);
915        assert_eq!(
916            K8sConditionStatus::from_wire_str(&ready.status),
917            Some(K8sConditionStatus::True),
918        );
919        let not_ready = ProcessCondition::not_ready("R", "why");
920        assert_eq!(
921            K8sConditionStatus::from_wire_str(&not_ready.status),
922            Some(K8sConditionStatus::False),
923        );
924        let attested = ProcessCondition::attested("blake3:zzz");
925        assert_eq!(
926            K8sConditionStatus::from_wire_str(&attested.status),
927            Some(K8sConditionStatus::True),
928        );
929    }
930
931    // ─── ProcessCondition::new_at_now backbone-composer substrate pins ───
932    //
933    // The composer [`ProcessCondition::new_at_now`] owns the 5-slot wire-
934    // shape backbone every sibling constructor on this impl block
935    // ([`ProcessCondition::ready`], [`ProcessCondition::not_ready`],
936    // [`ProcessCondition::attested`]) hand-authored pre-lift. These pins
937    // bind the observable slots (typed-enum → wire-string projection on
938    // `type_` + `status`, `Utc::now()` stamp on `last_transition_time`,
939    // `Some(<reason>.into())` wrap on `reason`, verbatim message opt on
940    // `message`) at fail-before-pass-after granularity so a regression
941    // that drifted any slot's projection (a typed-enum arm bypassed by
942    // an inline literal, the `Some(...)`-wrap on `reason` narrowed to
943    // a bare `.into()`, the `last_transition_time` stamp swapped for a
944    // fixed anchor) surfaces HERE rather than as silent operator-facing
945    // K8s-Condition wire-shape skew across the three sibling
946    // constructor sites.
947
948    /// Fail-before-pass-after: the composer's 5-slot output is byte-
949    /// identical (modulo the `Utc::now()` monotonic clock stamp) to
950    /// the pre-lift hand-authored struct literal every sibling
951    /// constructor restated. A regression that drifted the projection
952    /// at any of the four fixed slots surfaces HERE, not as silent
953    /// wire-form skew on the emitted Process CRD.
954    #[test]
955    fn new_at_now_composes_typed_enums_reason_option_wrap_and_message_slot_verbatim() {
956        let before = Utc::now();
957        let c = ProcessCondition::new_at_now(
958            ProcessConditionType::Ready,
959            K8sConditionStatus::True,
960            "ObservedRunning",
961            Some("healthy".into()),
962        );
963        let after = Utc::now();
964        assert_eq!(c.type_, ProcessConditionType::Ready.as_wire_str());
965        assert_eq!(c.status, K8sConditionStatus::True.as_wire_str());
966        assert_eq!(c.reason.as_deref(), Some("ObservedRunning"));
967        assert_eq!(c.message.as_deref(), Some("healthy"));
968        assert!(
969            c.last_transition_time >= before && c.last_transition_time <= after,
970            "last_transition_time rides Utc::now() at the composer body",
971        );
972    }
973
974    /// Fail-before-pass-after: the composer preserves an explicit
975    /// `None` message-slot verbatim — a K8s Condition with an absent
976    /// message vs an empty-string message are distinct wire shapes
977    /// the K8s API server treats differently, so the composer must
978    /// route `None` through without an accidental `Some(String::
979    /// new())` wrap.
980    #[test]
981    fn new_at_now_preserves_none_message_slot_verbatim() {
982        let c = ProcessCondition::new_at_now(
983            ProcessConditionType::Ready,
984            K8sConditionStatus::False,
985            "R",
986            None,
987        );
988        assert!(
989            c.message.is_none(),
990            "message-slot rides `None` verbatim — an empty-Some wrap is a distinct wire shape",
991        );
992    }
993
994    /// Byte-shape parity witness — `ProcessCondition::ready` routes
995    /// through `new_at_now(Ready, True, reason, message)`, matching
996    /// the pre-lift hand-authored 5-slot struct literal on every
997    /// observable slot except the monotonic `last_transition_time`
998    /// stamp.
999    #[test]
1000    fn ready_routes_through_new_at_now_composer_slots_match_pre_lift_literal() {
1001        let via_public = ProcessCondition::ready("ObservedRunning", Some("healthy".into()));
1002        let via_composer = ProcessCondition::new_at_now(
1003            ProcessConditionType::Ready,
1004            K8sConditionStatus::True,
1005            "ObservedRunning",
1006            Some("healthy".into()),
1007        );
1008        assert_eq!(via_public.type_, via_composer.type_);
1009        assert_eq!(via_public.status, via_composer.status);
1010        assert_eq!(via_public.reason, via_composer.reason);
1011        assert_eq!(via_public.message, via_composer.message);
1012    }
1013
1014    /// Byte-shape parity witness — `ProcessCondition::not_ready`
1015    /// routes through `new_at_now(Ready, False, reason, Some(message.
1016    /// into()))`, matching the pre-lift hand-authored 5-slot struct
1017    /// literal on every observable slot.
1018    #[test]
1019    fn not_ready_routes_through_new_at_now_composer_slots_match_pre_lift_literal() {
1020        let via_public = ProcessCondition::not_ready("ObservedFailed", "boom");
1021        let via_composer = ProcessCondition::new_at_now(
1022            ProcessConditionType::Ready,
1023            K8sConditionStatus::False,
1024            "ObservedFailed",
1025            Some("boom".into()),
1026        );
1027        assert_eq!(via_public.type_, via_composer.type_);
1028        assert_eq!(via_public.status, via_composer.status);
1029        assert_eq!(via_public.reason, via_composer.reason);
1030        assert_eq!(via_public.message, via_composer.message);
1031    }
1032
1033    /// Byte-shape parity witness — `ProcessCondition::attested`
1034    /// routes through `new_at_now(Attested, True, "AttestationWritten",
1035    /// Some(format!("composed_root={root}")))`, matching the pre-lift
1036    /// hand-authored 5-slot struct literal on every observable slot.
1037    #[test]
1038    fn attested_routes_through_new_at_now_composer_slots_match_pre_lift_literal() {
1039        let via_public = ProcessCondition::attested("blake3:abc123");
1040        let via_composer = ProcessCondition::new_at_now(
1041            ProcessConditionType::Attested,
1042            K8sConditionStatus::True,
1043            "AttestationWritten",
1044            Some("composed_root=blake3:abc123".into()),
1045        );
1046        assert_eq!(via_public.type_, via_composer.type_);
1047        assert_eq!(via_public.status, via_composer.status);
1048        assert_eq!(via_public.reason, via_composer.reason);
1049        assert_eq!(via_public.message, via_composer.message);
1050    }
1051
1052    /// Routing pin — the `type_` slot at the composer's `Self { …
1053    /// type_: type_.as_wire_str().into() }` binding rides through the
1054    /// [`ProcessConditionType::as_wire_str`] projection rather than an
1055    /// inline `format!("{:?}", type_)` or hand-authored per-variant
1056    /// literal. A regression that inlined the wire-string projection
1057    /// (drifting the composer off the closed-set owner + reopening the
1058    /// typo-drift surface a hand-authored `"ready"` / `"Ready "` /
1059    /// `"READY"` spelling would fall into silently) surfaces HERE
1060    /// rather than as silent per-variant wire-form skew where the
1061    /// composed Condition's `type_` slot disagrees with the K8s API
1062    /// server's expected literal.
1063    #[test]
1064    fn new_at_now_routes_type_slot_through_process_condition_type_as_wire_str() {
1065        for type_ in ProcessConditionType::ALL {
1066            let via_composer =
1067                ProcessCondition::new_at_now(type_, K8sConditionStatus::True, "R", None);
1068            assert_eq!(
1069                via_composer.type_,
1070                type_.as_wire_str(),
1071                "type_ slot must route through ProcessConditionType::as_wire_str for {type_:?}",
1072            );
1073        }
1074    }
1075
1076    /// Routing pin — the `status` slot at the composer's `Self { …
1077    /// status: status.as_wire_str().into() }` binding rides through
1078    /// the [`K8sConditionStatus::as_wire_str`] projection rather than
1079    /// an inline hand-authored per-variant literal. A regression that
1080    /// inlined the wire-string projection surfaces HERE rather than
1081    /// as silent per-variant wire-form skew where the composed
1082    /// Condition's `status` slot disagrees with the K8s API server's
1083    /// expected `"True"` / `"False"` / `"Unknown"` closed set.
1084    #[test]
1085    fn new_at_now_routes_status_slot_through_k8s_condition_status_as_wire_str() {
1086        for status in K8sConditionStatus::ALL {
1087            let via_composer =
1088                ProcessCondition::new_at_now(ProcessConditionType::Ready, status, "R", None);
1089            assert_eq!(
1090                via_composer.status,
1091                status.as_wire_str(),
1092                "status slot must route through K8sConditionStatus::as_wire_str for {status:?}",
1093            );
1094        }
1095    }
1096
1097    // ─── ProcessCondition::new_at clock-injectable substrate pins ────
1098    //
1099    // The clock-injectable substrate peer [`ProcessCondition::new_at`]
1100    // owns the 5-slot wire-shape backbone with `last_transition_time`
1101    // sourced from a caller-supplied `DateTime<Utc>` slot instead of
1102    // an inline `Utc::now()` read. [`ProcessCondition::new_at_now`]
1103    // delegates through it with `Utc::now()`. These pins bind:
1104    // (a) the `at` slot is stamped bytewise (no clamp/round/offset
1105    //     drift at the composer body),
1106    // (b) [`ProcessCondition::new_at_now`] composes through the
1107    //     substrate with a wall-clock third arg (a regression that
1108    //     re-inlined the 5-slot literal at `new_at_now` bypasses the
1109    //     substrate and surfaces here rather than as silent skew
1110    //     between `new_at`-anchored callers and `new_at_now`),
1111    // (c) the four non-clock slots ride through the SAME projections
1112    //     `new_at_now`'s existing pins already bind — closed-set
1113    //     `as_wire_str` at type_/status, `Some(<r>.into())` wrap at
1114    //     reason, verbatim `Option<String>` pass-through at message.
1115
1116    /// Fail-before-pass-after: the clock-injectable substrate peer
1117    /// [`ProcessCondition::new_at`] stamps the caller-supplied
1118    /// `DateTime<Utc>` at the `last_transition_time` slot bytewise —
1119    /// a deterministic anchor rides through with no wall-clock read,
1120    /// no clamp, no `Duration::seconds(0)` rounding, no offset
1121    /// normalization. Peer of the `[before, after]` bracket pin on
1122    /// [`ProcessCondition::new_at_now`]; this pin owns the
1123    /// deterministic-anchor half of the (wall-clock, deterministic-
1124    /// anchor) axis pair. A regression that inserted an implicit
1125    /// `Utc::now()` fallback at the composer body (silently ignoring
1126    /// the caller's anchor) surfaces HERE rather than as a hidden
1127    /// non-determinism at every future
1128    /// `ProcessCondition::new_at`-anchored test / controller-side
1129    /// callsite that pinned a specific anchor.
1130    #[test]
1131    fn new_at_binds_at_slot_to_supplied_datetime_verbatim() {
1132        let anchor: DateTime<Utc> = crate::time::at_epoch_second(1_700_000_000);
1133        let c = ProcessCondition::new_at(
1134            ProcessConditionType::Ready,
1135            K8sConditionStatus::True,
1136            "R",
1137            Some("m".into()),
1138            anchor,
1139        );
1140        assert_eq!(
1141            c.last_transition_time, anchor,
1142            "last_transition_time must ride the caller-supplied `at` slot bytewise — \
1143             a regression that inserted an implicit `Utc::now()` fallback would surface here",
1144        );
1145        // Cross-anchor pin — a distant future anchor + the epoch also
1146        // ride through bytewise, so the invariant holds at both
1147        // extremes of the wall-clock axis rather than only at the
1148        // 1.7-billion-second-past-epoch mid-range corner above. The
1149        // upper bound stays inside `chrono`'s valid `DateTime<Utc>`
1150        // range (year 2262 ceiling for nanosecond-precision anchors
1151        // the substrate carries) so the epoch composer succeeds.
1152        for anchor in [
1153            crate::time::at_epoch_second(0),
1154            crate::time::at_epoch_second(4_000_000_000),
1155        ] {
1156            let c = ProcessCondition::new_at(
1157                ProcessConditionType::Attested,
1158                K8sConditionStatus::True,
1159                "R",
1160                None,
1161                anchor,
1162            );
1163            assert_eq!(c.last_transition_time, anchor);
1164        }
1165    }
1166
1167    /// Cross-primitive coherence pin — [`ProcessCondition::new_at_now`]
1168    /// composes through [`ProcessCondition::new_at`] with `Utc::now()`
1169    /// at the `at` slot. Every non-clock slot on the two composers'
1170    /// outputs is byte-identical for the SAME (type_, status, reason,
1171    /// message) input tuple; only the `last_transition_time` differs
1172    /// (the wall-clock peer's slot falls in `[before, after]`, and the
1173    /// deterministic peer's slot is exactly the caller's anchor). A
1174    /// regression that re-inlined the 5-slot literal at
1175    /// [`ProcessCondition::new_at_now`] (bypassing the substrate) or
1176    /// swapped `Utc::now()` for a fixed anchor at the delegation body
1177    /// surfaces HERE rather than as silent skew between the two peer
1178    /// composers' consumers.
1179    #[test]
1180    fn new_at_now_routes_through_new_at_with_utc_now_stamp_bytewise() {
1181        let before = Utc::now();
1182        let via_wall = ProcessCondition::new_at_now(
1183            ProcessConditionType::Ready,
1184            K8sConditionStatus::False,
1185            "ObservedFailed",
1186            Some("boom".into()),
1187        );
1188        let after = Utc::now();
1189        // Anchor a peer of `via_wall` at a fixed deterministic slot
1190        // and pin non-clock parity; the two composers must agree on
1191        // every slot except `last_transition_time`.
1192        let via_at = ProcessCondition::new_at(
1193            ProcessConditionType::Ready,
1194            K8sConditionStatus::False,
1195            "ObservedFailed",
1196            Some("boom".into()),
1197            crate::time::at_epoch_second(0),
1198        );
1199        assert_eq!(via_wall.type_, via_at.type_);
1200        assert_eq!(via_wall.status, via_at.status);
1201        assert_eq!(via_wall.reason, via_at.reason);
1202        assert_eq!(via_wall.message, via_at.message);
1203        assert!(
1204            via_wall.last_transition_time >= before && via_wall.last_transition_time <= after,
1205            "new_at_now must stamp Utc::now() at the delegation body — a regression that \
1206             swapped it for a fixed anchor surfaces here",
1207        );
1208        // Delegation identity — for every closed-set (type_, status)
1209        // pair, composing through `new_at` with an anchor threaded
1210        // through `new_at_now`'s bracket yields identical non-clock
1211        // slots on both peers. Sweep the two closed-set axes
1212        // exhaustively so a future variant addition inherits the pin.
1213        for type_ in ProcessConditionType::ALL {
1214            for status in K8sConditionStatus::ALL {
1215                let via_wall = ProcessCondition::new_at_now(type_, status, "R", None);
1216                let via_at = ProcessCondition::new_at(
1217                    type_,
1218                    status,
1219                    "R",
1220                    None,
1221                    via_wall.last_transition_time,
1222                );
1223                assert_eq!(via_wall.type_, via_at.type_);
1224                assert_eq!(via_wall.status, via_at.status);
1225                assert_eq!(via_wall.reason, via_at.reason);
1226                assert_eq!(via_wall.message, via_at.message);
1227                assert_eq!(via_wall.last_transition_time, via_at.last_transition_time);
1228            }
1229        }
1230    }
1231
1232    /// Fail-before-pass-after: the clock-injectable substrate peer
1233    /// preserves an explicit `None` message-slot verbatim — mirror of
1234    /// the sibling pin on [`ProcessCondition::new_at_now`]. A K8s
1235    /// Condition with an absent message vs an empty-string message
1236    /// are distinct wire shapes the K8s API server treats
1237    /// differently, so a regression at the substrate body that
1238    /// wrapped `None` into `Some(String::new())` would surface HERE
1239    /// as well as at the wall-clock peer's pin.
1240    #[test]
1241    fn new_at_preserves_none_message_slot_verbatim() {
1242        let c = ProcessCondition::new_at(
1243            ProcessConditionType::Ready,
1244            K8sConditionStatus::False,
1245            "R",
1246            None,
1247            crate::time::at_epoch_second(0),
1248        );
1249        assert!(
1250            c.message.is_none(),
1251            "message-slot rides `None` verbatim — an empty-Some wrap is a distinct wire shape",
1252        );
1253    }
1254
1255    // ─── RenderedResourceCoords substrate pins ──────────────────────
1256
1257    #[test]
1258    fn rendered_resource_coords_from_json_extracts_all_four_slots_when_present() {
1259        let res = json!({
1260            "apiVersion": "kustomize.toolkit.fluxcd.io/v1",
1261            "kind": "Kustomization",
1262            "metadata": {
1263                "name": "observability-stack",
1264                "namespace": "flux-system",
1265            },
1266        });
1267        let c = RenderedResourceCoords::from_json(&res).expect("extract");
1268        assert_eq!(c.api_version, "kustomize.toolkit.fluxcd.io/v1");
1269        assert_eq!(c.kind, "Kustomization");
1270        assert_eq!(c.name, "observability-stack");
1271        assert_eq!(c.namespace.as_deref(), Some("flux-system"));
1272    }
1273
1274    #[test]
1275    fn rendered_resource_coords_from_json_captures_absent_namespace_as_none() {
1276        // Cluster-scoped resource — `metadata.namespace` intentionally absent.
1277        let res = json!({
1278            "apiVersion": "v1",
1279            "kind": "Namespace",
1280            "metadata": {"name": "demo-test"},
1281        });
1282        let c = RenderedResourceCoords::from_json(&res).expect("extract");
1283        assert_eq!(c.namespace, None);
1284        assert_eq!(c.name, "demo-test");
1285    }
1286
1287    #[test]
1288    fn rendered_resource_coords_from_json_errors_on_missing_api_version() {
1289        let res = json!({"kind": "K", "metadata": {"name": "n"}});
1290        let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
1291        assert_eq!(e.to_string(), "rendered resource missing apiVersion");
1292    }
1293
1294    #[test]
1295    fn rendered_resource_coords_from_json_errors_on_missing_kind() {
1296        let res = json!({"apiVersion": "v1", "metadata": {"name": "n"}});
1297        let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
1298        assert_eq!(e.to_string(), "rendered resource missing kind");
1299    }
1300
1301    #[test]
1302    fn rendered_resource_coords_from_json_errors_on_missing_metadata_name() {
1303        let res = json!({"apiVersion": "v1", "kind": "K", "metadata": {}});
1304        let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
1305        assert_eq!(e.to_string(), "rendered resource missing metadata.name");
1306    }
1307
1308    #[test]
1309    fn rendered_resource_coords_from_json_errors_on_missing_metadata_object() {
1310        // `metadata` absent entirely — same failure as `metadata.name` missing,
1311        // because the API-path leaf segment cannot be resolved.
1312        let res = json!({"apiVersion": "v1", "kind": "K"});
1313        let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
1314        assert_eq!(e.to_string(), "rendered resource missing metadata.name");
1315    }
1316
1317    #[test]
1318    fn rendered_resource_coords_from_json_errors_on_non_string_slot() {
1319        // A numeric `apiVersion` slot falls through the `.as_str()` gate and
1320        // triggers the same missing-slot failure as absence — the API-path
1321        // segment is not a string.
1322        let res = json!({
1323            "apiVersion": 42,
1324            "kind": "K",
1325            "metadata": {"name": "n"},
1326        });
1327        let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
1328        assert_eq!(e.to_string(), "rendered resource missing apiVersion");
1329    }
1330
1331    #[test]
1332    fn rendered_resource_coords_error_wording_is_canonical() {
1333        // Pins the exact spelling every downstream consumer sees.
1334        // Pre-lift wording differed across the two call sites (`"resource
1335        // missing X"` in `apply_owned` vs `"rendered resource missing X"` in
1336        // `flux_ref_from_json`); post-lift the canonical wording is
1337        // `"rendered resource missing X"` at every site.
1338        let cases = [
1339            (
1340                "apiVersion",
1341                json!({"kind": "K", "metadata": {"name": "n"}}),
1342            ),
1343            (
1344                "kind",
1345                json!({"apiVersion": "v1", "metadata": {"name": "n"}}),
1346            ),
1347            (
1348                "metadata.name",
1349                json!({"apiVersion": "v1", "kind": "K", "metadata": {}}),
1350            ),
1351        ];
1352        for (slot, res) in cases {
1353            let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
1354            assert_eq!(
1355                e.to_string(),
1356                format!("rendered resource missing {slot}"),
1357                "slot {slot} error must be canonical"
1358            );
1359        }
1360    }
1361
1362    // ─── RenderedResourceCoords::required_str substrate pins ────────
1363    //
1364    // Fail-before-pass-after granularity: the
1365    // `RenderedResourceCoords::required_str` inherent associated
1366    // function did not exist before this commit, so each test below
1367    // fails to compile pre-lift. Post-lift they collectively pin the
1368    // required-string-extract shape at ONE substrate owner — a
1369    // regression that swaps `MISSING_MESSAGE_PREFIX`, decouples the
1370    // `key` / `error_slot` slot pair with a wrong ordering, drops the
1371    // `str::to_string` coerce (returning `&str` and forcing every
1372    // consumer to re-stamp `.to_string()` per site), or narrows the
1373    // receiver from `Option<&Value>` to `&Value` (silently breaking
1374    // the `metadata.name` corner where the caller threads the
1375    // `res.get("metadata")` result directly) surfaces HERE rather
1376    // than as silent operator-facing skew across the three pre-lift
1377    // consumers on `from_json`.
1378
1379    #[test]
1380    fn required_str_present_string_slot_returns_owned_string() {
1381        // Ok-arm invariant: a present string slot at `key` on a
1382        // `Some(&Value::Object)` receiver returns `Ok(<owned>)` —
1383        // the primitive absorbs the `.to_string()` coerce the three
1384        // pre-lift restatements each stamped at the tail.
1385        let res = json!({"apiVersion": "kustomize.toolkit.fluxcd.io/v1"});
1386        let got =
1387            RenderedResourceCoords::required_str(Some(&res), "apiVersion", "apiVersion").unwrap();
1388        assert_eq!(got, "kustomize.toolkit.fluxcd.io/v1");
1389    }
1390
1391    #[test]
1392    fn required_str_none_receiver_errors_with_canonical_wire_form() {
1393        // Absent-shape corner 1: the caller threads `None`
1394        // (`res.get("metadata")` returned `None` because the top-
1395        // level `metadata` slot itself is absent). The primitive
1396        // errors with the SAME wire form the two other absent
1397        // corners produce, keeping the operator-facing footprint
1398        // singular.
1399        let e = RenderedResourceCoords::required_str(None, "name", "metadata.name")
1400            .expect_err("None receiver must error");
1401        assert_eq!(e.to_string(), "rendered resource missing metadata.name");
1402    }
1403
1404    #[test]
1405    fn required_str_absent_slot_errors_with_canonical_wire_form() {
1406        // Absent-shape corner 2: the receiver is present but the
1407        // slot at `key` is not stamped on it. Wire form matches
1408        // the `None`-receiver corner and the non-string corner.
1409        let res = json!({"kind": "K"});
1410        let e = RenderedResourceCoords::required_str(Some(&res), "apiVersion", "apiVersion")
1411            .expect_err("absent slot must error");
1412        assert_eq!(e.to_string(), "rendered resource missing apiVersion");
1413    }
1414
1415    #[test]
1416    fn required_str_non_string_slot_errors_with_canonical_wire_form() {
1417        // Absent-shape corner 3: the slot is present but stamped
1418        // as a JSON number / bool / object / array — every
1419        // non-`Value::String` variant falls through the underlying
1420        // `get_str` gate and produces the SAME `"missing"` diagnostic.
1421        // Pinning EVERY non-string variant here (not just number)
1422        // guarantees an operator's error-stream grep collapses all
1423        // fixture-authoring bugs at this slot onto one footprint.
1424        for bad in [
1425            json!({"apiVersion": 42}),
1426            json!({"apiVersion": true}),
1427            json!({"apiVersion": {}}),
1428            json!({"apiVersion": [1]}),
1429            json!({"apiVersion": null}),
1430        ] {
1431            let e = RenderedResourceCoords::required_str(Some(&bad), "apiVersion", "apiVersion")
1432                .expect_err("non-string slot must error");
1433            assert_eq!(e.to_string(), "rendered resource missing apiVersion");
1434        }
1435    }
1436
1437    #[test]
1438    fn required_str_non_object_receiver_errors_with_canonical_wire_form() {
1439        // Absent-shape corner 4: the receiver itself is not a
1440        // `Value::Object` — a resource authored as a JSON array,
1441        // string, or null at any of the levels the primitive
1442        // walks. The underlying `get_str` step returns `None`
1443        // verbatim (matching the pre-lift chain's own behavior)
1444        // and the primitive stamps the canonical wire form.
1445        for bad in [json!([1, 2, 3]), json!("stringified"), Value::Null] {
1446            let e = RenderedResourceCoords::required_str(Some(&bad), "name", "metadata.name")
1447                .expect_err("non-object receiver must error");
1448            assert_eq!(e.to_string(), "rendered resource missing metadata.name");
1449        }
1450    }
1451
1452    #[test]
1453    fn required_str_decouples_key_from_error_slot_at_metadata_name_shape() {
1454        // Slot-decoupling pin: for the `metadata.name` corner the
1455        // primitive looks up `key = "name"` on the metadata sub-
1456        // object while stamping `error_slot = "metadata.name"` into
1457        // the error's `Display` output — the two are NOT the same
1458        // string, and a regression that collapsed them (using
1459        // `key` for both the lookup AND the error slug, or
1460        // vice-versa) would silently pass the shallow `apiVersion`
1461        // / `kind` pins above (where `key == error_slot`) and fail
1462        // HERE. Present-arm: lookup succeeds on the metadata sub-
1463        // object's `name` slot, returns the owned string.
1464        let res = json!({"metadata": {"name": "demo"}});
1465        let metadata = res.get("metadata");
1466        let got = RenderedResourceCoords::required_str(metadata, "name", "metadata.name").unwrap();
1467        assert_eq!(got, "demo");
1468        // Absent-arm: same slot-decoupling but the `name` sub-slot
1469        // is absent — the error slug is the DOTTED path, not the
1470        // shallow `"name"` key.
1471        let res_no_name = json!({"metadata": {}});
1472        let metadata_empty = res_no_name.get("metadata");
1473        let e = RenderedResourceCoords::required_str(metadata_empty, "name", "metadata.name")
1474            .expect_err("absent metadata.name must error");
1475        assert_eq!(e.to_string(), "rendered resource missing metadata.name");
1476    }
1477
1478    #[test]
1479    fn required_str_error_wire_form_composes_missing_message_prefix_verbatim() {
1480        // Wire-form composition pin: the error's `Display` is
1481        // exactly `"<Self::MISSING_MESSAGE_PREFIX> <error_slot>"` —
1482        // the leading prefix comes from the `const` owner + a
1483        // single space + the caller-supplied slug. A regression
1484        // that switched the separator (a colon, an em-dash) or
1485        // dropped the prefix (returning just the slot slug) would
1486        // silently invert every operator-facing log grep footprint;
1487        // this pin binds the composition to the ONE prefix const
1488        // so a future rename lands atomically at both the source
1489        // and the pins.
1490        let e = RenderedResourceCoords::required_str(None, "name", "metadata.name")
1491            .expect_err("None receiver must error");
1492        let expected = format!(
1493            "{prefix} metadata.name",
1494            prefix = RenderedResourceCoords::MISSING_MESSAGE_PREFIX,
1495        );
1496        assert_eq!(e.to_string(), expected);
1497    }
1498
1499    #[test]
1500    fn required_str_shape_parity_matches_pre_lift_hand_authored_chain_bytewise() {
1501        // Byte-shape parity pin: on every corner (present, absent,
1502        // non-string, non-object, None-receiver) the primitive's
1503        // output MUST match the pre-lift hand-authored
1504        // `.get_str(<key>).ok_or_else(|| anyhow!("rendered resource
1505        // missing <slot>"))?.to_string()` chain bytewise — the
1506        // Ok-arm string equals the raw `get_str` slice as an owned
1507        // `String`, and the Err-arm `Display` equals the pre-lift
1508        // `anyhow!(...)` output verbatim. A regression that inserted
1509        // a normalization (a trim, an NFC-fold) into the Ok arm or
1510        // altered the diagnostic wrapping in the Err arm surfaces
1511        // HERE rather than as silent per-consumer schema drift.
1512        let cases: &[(Value, &'static str, &'static str)] = &[
1513            (json!({"apiVersion": "v1"}), "apiVersion", "apiVersion"),
1514            (json!({"kind": "K"}), "kind", "kind"),
1515        ];
1516        for (res, key, error_slot) in cases {
1517            let via_primitive =
1518                RenderedResourceCoords::required_str(Some(res), key, error_slot).unwrap();
1519            let via_pre_lift = res.get_str(key).unwrap().to_string();
1520            assert_eq!(via_primitive, via_pre_lift);
1521        }
1522        let empty = json!({"other": "value"});
1523        let err_via_primitive =
1524            RenderedResourceCoords::required_str(Some(&empty), "apiVersion", "apiVersion")
1525                .expect_err("absent slot must error");
1526        let err_via_pre_lift = anyhow::anyhow!("rendered resource missing apiVersion");
1527        assert_eq!(err_via_primitive.to_string(), err_via_pre_lift.to_string());
1528    }
1529
1530    #[test]
1531    fn required_str_missing_message_prefix_matches_pre_lift_wire_form_verbatim() {
1532        // Const-owner pin: the pre-lift hand-authored `anyhow!("rendered
1533        // resource missing X")` restatements each embedded the leading
1534        // `"rendered resource missing"` prefix as an inline literal.
1535        // Post-lift the prefix lives at ONE const owner — a rename lands
1536        // there and the three consumers on `from_json` inherit the
1537        // rename mechanically. This pin binds the const to the pre-lift
1538        // spelling so a rename shows up at BOTH the const definition
1539        // AND this pin as a coherent atomic edit, not as a silent
1540        // diff between the const and its downstream consumers.
1541        assert_eq!(
1542            RenderedResourceCoords::MISSING_MESSAGE_PREFIX,
1543            "rendered resource missing",
1544        );
1545    }
1546
1547    #[test]
1548    fn rendered_resource_coords_namespace_or_default_returns_slice_when_some() {
1549        let c = RenderedResourceCoords {
1550            api_version: "v1".into(),
1551            kind: "K".into(),
1552            name: "n".into(),
1553            namespace: Some("prod".into()),
1554        };
1555        assert_eq!(c.namespace_or_default(), "prod");
1556    }
1557
1558    #[test]
1559    fn rendered_resource_coords_namespace_or_default_falls_back_when_none() {
1560        let c = RenderedResourceCoords {
1561            api_version: "v1".into(),
1562            kind: "K".into(),
1563            name: "n".into(),
1564            namespace: None,
1565        };
1566        assert_eq!(c.namespace_or_default(), Process::DEFAULT_NAMESPACE);
1567        assert_eq!(c.namespace_or_default(), "default");
1568    }
1569
1570    // ─── FluxResourceRef::fetch_coords substrate pins ─────────────
1571    //
1572    // The 4-slot `(&namespace, &api_version, &kind, &name)` borrow
1573    // projection lifts the pre-existing 5-slot `ssapply::fetch(client,
1574    // &r.namespace, &r.api_version, &r.kind, &r.name)` splat that
1575    // recurred at TWO hand-authored sites in
1576    // `tatara-reconciler::phase_machine` (`handle_running`,
1577    // `handle_attested`) past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
1578    // trigger. These pins bind the slot order at fail-before-pass-
1579    // after granularity so a regression that swapped `namespace` and
1580    // `api_version` (both `String`, mechanically interchangeable to
1581    // a bad refactor) surfaces HERE rather than as a silent wire-time
1582    // 404 at every downstream Flux fetch consumer.
1583
1584    fn sample_flux_ref() -> FluxResourceRef {
1585        // Slot values are deliberately distinct so a swap between any
1586        // two adjacent tuple positions surfaces as an equality
1587        // failure at the assertion site — a slot-inversion regression
1588        // cannot masquerade as identity by accident.
1589        FluxResourceRef {
1590            api_version: "kustomize.toolkit.fluxcd.io/v1".to_string(),
1591            kind: "Kustomization".to_string(),
1592            name: "observability-stack".to_string(),
1593            namespace: "flux-system".to_string(),
1594            ready: true,
1595            message: None,
1596            last_check: None,
1597        }
1598    }
1599
1600    #[test]
1601    fn flux_resource_ref_fetch_coords_binds_slots_by_position() {
1602        // Positional pin: the 4-tuple return binds
1603        // `(namespace, api_version, kind, name)` in THAT order,
1604        // matching the raw `ssapply::fetch(client, ns, av, kind,
1605        // name)` positional signature every pre-lift callsite splatted
1606        // into. A regression that swapped ANY pair of adjacent slots
1607        // (all four axes are `String` and mechanically
1608        // indistinguishable at the type level) would surface here
1609        // rather than as an operator-visible wire-form 404 at every
1610        // downstream fetch consumer.
1611        let r = sample_flux_ref();
1612        let (ns, av, kind, name) = r.fetch_coords();
1613        assert_eq!(ns, "flux-system", "position 0 must be namespace");
1614        assert_eq!(
1615            av, "kustomize.toolkit.fluxcd.io/v1",
1616            "position 1 must be api_version"
1617        );
1618        assert_eq!(kind, "Kustomization", "position 2 must be kind");
1619        assert_eq!(name, "observability-stack", "position 3 must be name");
1620    }
1621
1622    #[test]
1623    fn flux_resource_ref_fetch_coords_returns_borrows_of_owned_slots() {
1624        // Borrow-discipline pin: the 4-tuple returns `&str` borrows
1625        // of the enclosing `FluxResourceRef`'s owned `String` slots —
1626        // NOT a fresh allocation or a clone. A regression that
1627        // switched the projection to owned strings (via `.clone()` or
1628        // `format!`) would defeat the zero-copy contract and would
1629        // surface here via pointer-identity comparison.
1630        let r = sample_flux_ref();
1631        let (ns, av, kind, name) = r.fetch_coords();
1632        assert!(std::ptr::eq(ns.as_ptr(), r.namespace.as_ptr()));
1633        assert!(std::ptr::eq(av.as_ptr(), r.api_version.as_ptr()));
1634        assert!(std::ptr::eq(kind.as_ptr(), r.kind.as_ptr()));
1635        assert!(std::ptr::eq(name.as_ptr(), r.name.as_ptr()));
1636    }
1637
1638    #[test]
1639    fn flux_resource_ref_fetch_coords_is_a_pure_borrow_projection() {
1640        // Purity pin: calling the projection twice on the same ref
1641        // returns byte-identical slices (same pointer, same length).
1642        // A regression that introduced state — a lazy-cached slot
1643        // computed on first call, a normalization step that ran once
1644        // and cached — would surface here rather than as silent drift
1645        // between the VERIFY-phase and ATTEST-heartbeat consumers on
1646        // the SAME ref within one reconcile pass.
1647        let r = sample_flux_ref();
1648        let a = r.fetch_coords();
1649        let b = r.fetch_coords();
1650        assert!(std::ptr::eq(a.0.as_ptr(), b.0.as_ptr()));
1651        assert!(std::ptr::eq(a.1.as_ptr(), b.1.as_ptr()));
1652        assert!(std::ptr::eq(a.2.as_ptr(), b.2.as_ptr()));
1653        assert!(std::ptr::eq(a.3.as_ptr(), b.3.as_ptr()));
1654    }
1655
1656    #[test]
1657    fn flux_resource_ref_fetch_coords_ignores_status_slots() {
1658        // Coverage pin: the projection exposes ONLY the four API-path
1659        // slots the fetch call requires; the ref's status slots
1660        // (`ready`, `message`, `last_check`) are deliberately absent
1661        // from the tuple. The fetch signature admits four `&str`
1662        // slots, and the projection carries EXACTLY those four — no
1663        // silent widening that would surface as an arity mismatch at
1664        // every downstream `fetch(...)` call.
1665        let r = sample_flux_ref();
1666        let coords = r.fetch_coords();
1667        assert_eq!(
1668            std::mem::size_of_val(&coords),
1669            std::mem::size_of::<(&str, &str, &str, &str)>(),
1670            "the 4-tuple width must match the raw fetch signature's four `&str` slots"
1671        );
1672    }
1673
1674    // ─── FluxResourceRef::observed substrate pins ─────────────────
1675    //
1676    // The 6-arg composer stamps `last_check` at ONE substrate site
1677    // (the pre-lift 7-slot struct-literal restated `Some(chrono::
1678    // Utc::now())` at TWO hand-authored sites in
1679    // `tatara-reconciler::phase_machine` — `handle_running`'s per-
1680    // ref VERIFY rebuild and `flux_ref_from_json`'s post-SSA
1681    // seeder). These pins bind the six input slots by position so a
1682    // regression that swapped `api_version` and `kind` (both
1683    // `String`, mechanically interchangeable to a bad refactor)
1684    // surfaces HERE rather than as a silent wire-time 404 at every
1685    // downstream fetch consumer.
1686    //
1687    // Every test constructs distinct values across the four
1688    // `String` coordinate slots so a slot swap fails structurally
1689    // rather than by accident of matching literals.
1690
1691    #[test]
1692    fn flux_resource_ref_observed_binds_slots_by_position() {
1693        // Positional pin: the 6-arg constructor binds
1694        // `(api_version, kind, name, namespace, ready, message)`
1695        // in THAT order, matching the pre-lift 7-slot struct-
1696        // literal's declaration order. A regression that swapped
1697        // ANY pair of adjacent `String` coordinate slots (all four
1698        // are mechanically indistinguishable at the type level)
1699        // would surface here rather than as a wire-time 404 at
1700        // every downstream Flux fetch consumer.
1701        let r = FluxResourceRef::observed(
1702            "kustomize.toolkit.fluxcd.io/v1".to_string(),
1703            "Kustomization".to_string(),
1704            "observability-stack".to_string(),
1705            "flux-system".to_string(),
1706            true,
1707            Some("healthy".to_string()),
1708        );
1709        assert_eq!(r.api_version, "kustomize.toolkit.fluxcd.io/v1");
1710        assert_eq!(r.kind, "Kustomization");
1711        assert_eq!(r.name, "observability-stack");
1712        assert_eq!(r.namespace, "flux-system");
1713        assert!(r.ready);
1714        assert_eq!(r.message.as_deref(), Some("healthy"));
1715    }
1716
1717    #[test]
1718    fn flux_resource_ref_observed_stamps_last_check_at_now() {
1719        // Stamp pin: the `last_check` slot is filled with
1720        // `Some(<recent Utc>)` at the composer's body. A
1721        // regression that dropped the stamp (leaving `None`) or
1722        // shifted it to a stale constant would surface here rather
1723        // than as silent operator-observed staleness at
1724        // `ProcessStatus.flux_resources` panels. Bounds the stamp
1725        // to within a generous 5s window of the composer call so
1726        // slow CI runners do not false-positive.
1727        let before = Utc::now();
1728        let r = FluxResourceRef::observed(
1729            "v1".to_string(),
1730            "K".to_string(),
1731            "n".to_string(),
1732            "ns".to_string(),
1733            false,
1734            None,
1735        );
1736        let after = Utc::now();
1737        let stamp = r.last_check.expect("observed must stamp last_check");
1738        assert!(stamp >= before, "stamp must be >= before-call `now`");
1739        assert!(stamp <= after, "stamp must be <= after-call `now`");
1740    }
1741
1742    #[test]
1743    fn flux_resource_ref_observed_round_trips_through_fetch_coords() {
1744        // Cross-composer coherence pin: a ref built by `observed`
1745        // then unpacked by `fetch_coords` returns the same four
1746        // slots in the peer projection's positional order
1747        // `(namespace, api_version, kind, name)`. Composition of
1748        // the two primitives on the same ref preserves the slot
1749        // identity — a regression at either end (a slot swap in
1750        // `observed`, or a slot swap in `fetch_coords`) would
1751        // surface here rather than as silent drift between the
1752        // writer and the reader on the same persisted slice.
1753        let r = FluxResourceRef::observed(
1754            "helm.toolkit.fluxcd.io/v2".to_string(),
1755            "HelmRelease".to_string(),
1756            "prometheus-op".to_string(),
1757            "monitoring".to_string(),
1758            false,
1759            Some("applied; awaiting reconciliation".to_string()),
1760        );
1761        let (ns, av, kind, name) = r.fetch_coords();
1762        assert_eq!(ns, "monitoring");
1763        assert_eq!(av, "helm.toolkit.fluxcd.io/v2");
1764        assert_eq!(kind, "HelmRelease");
1765        assert_eq!(name, "prometheus-op");
1766    }
1767
1768    #[test]
1769    fn flux_resource_ref_observed_matches_pre_lift_struct_literal_field_for_field() {
1770        // Byte-for-byte parity pin against the pre-lift 7-slot
1771        // struct-literal spelled at BOTH `phase_machine::
1772        // handle_running` and `phase_machine::flux_ref_from_json`.
1773        // A regression that reordered any of the six inputs at
1774        // the composer's argument list, or that swapped a
1775        // `ready`/`message` pair inside the composer's body,
1776        // would surface here rather than as silent divergence
1777        // between the composer's output and the pre-lift hand-
1778        // authored shape every persisted status writer restated.
1779        let composed = FluxResourceRef::observed(
1780            "source.toolkit.fluxcd.io/v1beta2".to_string(),
1781            "OCIRepository".to_string(),
1782            "chart-source".to_string(),
1783            "flux-system".to_string(),
1784            false,
1785            Some("applied; awaiting reconciliation".to_string()),
1786        );
1787        // Hand-authored the same seven slots directly, with a
1788        // held-open stamp window across the composer call.
1789        let stamped = composed.last_check.expect("stamped");
1790        let baseline = FluxResourceRef {
1791            api_version: "source.toolkit.fluxcd.io/v1beta2".to_string(),
1792            kind: "OCIRepository".to_string(),
1793            name: "chart-source".to_string(),
1794            namespace: "flux-system".to_string(),
1795            ready: false,
1796            message: Some("applied; awaiting reconciliation".to_string()),
1797            last_check: Some(stamped),
1798        };
1799        assert_eq!(composed.api_version, baseline.api_version);
1800        assert_eq!(composed.kind, baseline.kind);
1801        assert_eq!(composed.name, baseline.name);
1802        assert_eq!(composed.namespace, baseline.namespace);
1803        assert_eq!(composed.ready, baseline.ready);
1804        assert_eq!(composed.message, baseline.message);
1805        assert_eq!(composed.last_check, baseline.last_check);
1806    }
1807
1808    // ─── FluxResourceRef::pending substrate pins ─────────────────────
1809    //
1810    // Bind [`FluxResourceRef::pending`] at fail-before-pass-after
1811    // granularity so a regression that leaked a non-default status
1812    // slot (`ready: true`, `message: Some("something")`, `last_check:
1813    // Some(Utc::now())`), swapped two adjacent coordinate slots (all
1814    // four are `String` and mechanically interchangeable at the type
1815    // level), or diverged from the pre-lift 7-slot struct-literal on
1816    // any of the seven fields surfaces HERE rather than as silent
1817    // operator-invisible drift at the 3 downstream fixture consumers
1818    // (crd.rs `sample_flux_ref`, ssapply.rs `sample_flux_ref_for_diag`,
1819    // ssapply.rs `flux_ref_fetch_error_context_matches_pre_lift_...`).
1820    //
1821    // Each pin is fail-before-pass-after: the primitive did not exist
1822    // pre-lift, so any test that invokes it fails to compile pre-lift
1823    // and passes post-lift; the byte-identity pins below then bind
1824    // the specific shape choice.
1825
1826    #[test]
1827    fn flux_resource_ref_pending_binds_coordinate_slots_by_position() {
1828        // Positional pin: the 4-arg constructor binds `(api_version,
1829        // kind, name, namespace)` in THAT order, matching the pre-
1830        // lift 7-slot struct-literal's declaration order. A regression
1831        // that swapped ANY pair of adjacent `String` coordinate slots
1832        // (all four are mechanically indistinguishable at the type
1833        // level) would surface here rather than as a wire-time 404 at
1834        // every downstream Flux fetch consumer that walks
1835        // `FluxResourceRef.fetch_coords`.
1836        let r = FluxResourceRef::pending(
1837            "kustomize.toolkit.fluxcd.io/v1",
1838            "Kustomization",
1839            "observability-stack",
1840            "flux-system",
1841        );
1842        assert_eq!(r.api_version, "kustomize.toolkit.fluxcd.io/v1");
1843        assert_eq!(r.kind, "Kustomization");
1844        assert_eq!(r.name, "observability-stack");
1845        assert_eq!(r.namespace, "flux-system");
1846    }
1847
1848    #[test]
1849    fn flux_resource_ref_pending_defaults_every_status_slot() {
1850        // Default-slot pin: the three status slots (`ready`, `message`,
1851        // `last_check`) are ALL defaulted at the composer's body — no
1852        // wall-clock read, no non-`None` `message` leak, no `ready:
1853        // true` regression that would silently un-pend the fixture.
1854        // A regression that stamped `Some(Utc::now())` into
1855        // `last_check` (matching the sibling `observed` composer's
1856        // wall-clock read) would silently defeat the deterministic-
1857        // fixture contract the peer partition holds.
1858        let r = FluxResourceRef::pending("v1", "K", "n", "ns");
1859        assert!(
1860            !r.ready,
1861            "pending composer must default `ready` to false — non-`false` breaks the pre-observation contract"
1862        );
1863        assert_eq!(
1864            r.message, None,
1865            "pending composer must default `message` to None — non-`None` leaks a stale message into the pre-observation seed",
1866        );
1867        assert_eq!(
1868            r.last_check, None,
1869            "pending composer must default `last_check` to None — a `Some(_)` leak defeats the deterministic-peer partition against `observed`",
1870        );
1871    }
1872
1873    #[test]
1874    fn flux_resource_ref_pending_accepts_both_owned_and_borrowed_coordinates() {
1875        // The `impl Into<String>` ergonomic contract: both `&'static
1876        // str` literals (the fixture-helper sites that spell
1877        // coordinates inline) and owned `String` (a future callsite
1878        // handing off a dynamically-derived coordinate) round-trip
1879        // through the SAME composer signature without widening. A
1880        // regression that specialised the signature to one form or
1881        // the other would break either the inline-literal helpers or
1882        // the owned-`String` downstream consumers.
1883        let borrowed: FluxResourceRef = FluxResourceRef::pending("v1", "K", "n", "ns");
1884        let owned: FluxResourceRef = FluxResourceRef::pending(
1885            "v1".to_string(),
1886            "K".to_string(),
1887            "n".to_string(),
1888            "ns".to_string(),
1889        );
1890        assert_eq!(borrowed.api_version, owned.api_version);
1891        assert_eq!(borrowed.kind, owned.kind);
1892        assert_eq!(borrowed.name, owned.name);
1893        assert_eq!(borrowed.namespace, owned.namespace);
1894        assert_eq!(borrowed.ready, owned.ready);
1895        assert_eq!(borrowed.message, owned.message);
1896        assert_eq!(borrowed.last_check, owned.last_check);
1897    }
1898
1899    #[test]
1900    fn flux_resource_ref_pending_matches_pre_lift_struct_literal_bytewise() {
1901        // Byte-for-byte parity pin against the pre-lift 7-slot
1902        // struct-literal spelled at ALL THREE hand-authored fixture
1903        // sites (crd.rs `sample_flux_ref`, ssapply.rs
1904        // `sample_flux_ref_for_diag`, ssapply.rs inline in the
1905        // cross-substrate coherence pin's per-case sweep). Sweeps the
1906        // three representative coordinate tuples the pre-lift sites
1907        // used, so a regression that special-cased any one variant
1908        // (a `Kustomization`-only path via `if kind ==
1909        // "Kustomization" ...`) surfaces here.
1910        let cases = [
1911            (
1912                "kustomize.toolkit.fluxcd.io/v1",
1913                "Kustomization",
1914                "observability-stack",
1915                "flux-system",
1916            ),
1917            (
1918                "helm.toolkit.fluxcd.io/v2",
1919                "HelmRelease",
1920                "prometheus-op",
1921                "monitoring",
1922            ),
1923            (
1924                "source.toolkit.fluxcd.io/v1beta2",
1925                "OCIRepository",
1926                "chart-source",
1927                "flux-system",
1928            ),
1929        ];
1930        for (av, kind, name, ns) in cases {
1931            let composed = FluxResourceRef::pending(av, kind, name, ns);
1932            let hand_authored = FluxResourceRef {
1933                api_version: av.to_string(),
1934                kind: kind.to_string(),
1935                name: name.to_string(),
1936                namespace: ns.to_string(),
1937                ready: false,
1938                message: None,
1939                last_check: None,
1940            };
1941            assert_eq!(composed.api_version, hand_authored.api_version);
1942            assert_eq!(composed.kind, hand_authored.kind);
1943            assert_eq!(composed.name, hand_authored.name);
1944            assert_eq!(composed.namespace, hand_authored.namespace);
1945            assert_eq!(composed.ready, hand_authored.ready);
1946            assert_eq!(composed.message, hand_authored.message);
1947            assert_eq!(composed.last_check, hand_authored.last_check);
1948        }
1949    }
1950
1951    #[test]
1952    fn flux_resource_ref_pending_partitions_the_composer_axis_against_observed() {
1953        // Cross-composer partition pin: `pending` and `observed`
1954        // both produce `FluxResourceRef` but partition the composer
1955        // axis at the (deterministic-fixture, wall-clock-observed)
1956        // split — `pending` reads no clock and leaves `last_check:
1957        // None`, `observed` reads the wall clock and stamps
1958        // `last_check: Some(<recent Utc>)`. A regression that merged
1959        // either primitive onto the other (a `pending` that started
1960        // stamping `Utc::now()`, an `observed` that started leaving
1961        // `last_check: None`) would collapse the partition and
1962        // surface here.
1963        let p = FluxResourceRef::pending("v1", "K", "n", "ns");
1964        assert_eq!(
1965            p.last_check, None,
1966            "pending is deterministic — no clock read"
1967        );
1968        let o = FluxResourceRef::observed(
1969            "v1".to_string(),
1970            "K".to_string(),
1971            "n".to_string(),
1972            "ns".to_string(),
1973            false,
1974            None,
1975        );
1976        assert!(o.last_check.is_some(), "observed reads the wall clock");
1977    }
1978
1979    #[test]
1980    fn flux_resource_ref_pending_composes_with_fetch_coords_at_pre_observation_shape() {
1981        // Cross-composer coherence pin: a ref built by `pending`
1982        // then unpacked by `fetch_coords` returns the same four
1983        // slots in the peer projection's positional order
1984        // `(namespace, api_version, kind, name)`. Composition of
1985        // the two primitives on the same pre-observation ref
1986        // preserves the slot identity — a regression at either end
1987        // (a slot swap in `pending`, or a slot swap in
1988        // `fetch_coords`) would surface here rather than as silent
1989        // drift between the fixture writer and every downstream
1990        // fetch reader.
1991        let r = FluxResourceRef::pending(
1992            "helm.toolkit.fluxcd.io/v2",
1993            "HelmRelease",
1994            "prometheus-op",
1995            "monitoring",
1996        );
1997        let (ns, av, kind, name) = r.fetch_coords();
1998        assert_eq!(ns, "monitoring");
1999        assert_eq!(av, "helm.toolkit.fluxcd.io/v2");
2000        assert_eq!(kind, "HelmRelease");
2001        assert_eq!(name, "prometheus-op");
2002    }
2003
2004    #[test]
2005    fn rendered_resource_coords_namespace_fallback_shares_process_default_const() {
2006        // Byte-identity between the namespace fallback and the workspace-
2007        // wide `Process::DEFAULT_NAMESPACE` const. A regression that spelled
2008        // the fallback as any other string ("kube-system", "", "default-ns")
2009        // would silently drift between the coord-primitive family here and
2010        // the `Process`-borne family in `crd.rs` — surfaces here rather than
2011        // as operator-observed namespace routing skew between the two
2012        // primitive families.
2013        let c = RenderedResourceCoords {
2014            api_version: "v1".into(),
2015            kind: "K".into(),
2016            name: "n".into(),
2017            namespace: None,
2018        };
2019        assert_eq!(c.namespace_or_default(), Process::DEFAULT_NAMESPACE);
2020    }
2021
2022    // ─── CheckedCondition::all_satisfied substrate pins ─────────────
2023    //
2024    // Bind [`CheckedCondition::all_satisfied`] at fail-before-pass-
2025    // after granularity so a regression that flipped the fold
2026    // direction (`any` for `all`), inverted the projected bit
2027    // (`!c.satisfied`), reshaped the return form (an owned
2028    // `Vec<bool>` instead of the folded `bool`), or dropped the
2029    // vacuous-truth empty-slice corner surfaces HERE rather than as
2030    // silent operator-facing gate-flip at the reconciler's PROVE-
2031    // phase precondition gate + VERIFY-phase postcondition gate.
2032
2033    fn sample_checked(satisfied: bool) -> CheckedCondition {
2034        CheckedCondition {
2035            condition: crate::boundary::Condition {
2036                kind: crate::boundary::ConditionKind::ProcessPhase,
2037                params: serde_json::json!({}),
2038            },
2039            satisfied,
2040            last_check: None,
2041            message: None,
2042        }
2043    }
2044
2045    #[test]
2046    fn checked_condition_all_satisfied_returns_true_when_every_row_is_satisfied() {
2047        // Populated slice, every row `satisfied = true` — the RENDER-
2048        // phase advance corner: `handle_execing` proceeds to intent
2049        // dispatch iff every precondition holds.
2050        let checked = vec![
2051            sample_checked(true),
2052            sample_checked(true),
2053            sample_checked(true),
2054        ];
2055        assert!(
2056            CheckedCondition::all_satisfied(&checked),
2057            "all-satisfied slice must fold to true — a regression that inverted the bit would silently gate every RENDER advance behind an inverted predicate"
2058        );
2059    }
2060
2061    #[test]
2062    fn checked_condition_all_satisfied_returns_false_when_any_row_is_unsatisfied() {
2063        // Populated slice with ONE unsatisfied row — the heartbeat
2064        // requeue corner: `handle_running` stays in Running while any
2065        // postcondition remains unsatisfied.
2066        let mixed = vec![
2067            sample_checked(true),
2068            sample_checked(false),
2069            sample_checked(true),
2070        ];
2071        assert!(
2072            !CheckedCondition::all_satisfied(&mixed),
2073            "mixed slice must fold to false — a regression that folded via `any` instead of `all` would silently green-light every VERIFY advance"
2074        );
2075    }
2076
2077    #[test]
2078    fn checked_condition_all_satisfied_returns_false_when_every_row_is_unsatisfied() {
2079        // Populated slice with EVERY row unsatisfied — the tightest
2080        // gate corner: no phase advance is legal.
2081        let none_pass = vec![sample_checked(false), sample_checked(false)];
2082        assert!(
2083            !CheckedCondition::all_satisfied(&none_pass),
2084            "all-unsatisfied slice must fold to false"
2085        );
2086    }
2087
2088    #[test]
2089    fn checked_condition_all_satisfied_returns_true_on_empty_slice() {
2090        // Empty-slice vacuous-truth corner: `[T]::iter().all(_)`
2091        // returns `true` on empty input, and the pre-lift phase
2092        // gate's `if !preconditions.is_empty() { ... }` guard sat
2093        // BEFORE the fold, so the fold itself never saw an empty
2094        // slice in production. Post-lift the primitive absorbs the
2095        // empty corner cleanly — a caller that drops the outer
2096        // `is_empty()` guard (a future path that folds every gate
2097        // through this ONE primitive without a prior gate) still
2098        // sees the vacuous-truth semantics that match
2099        // [`Iterator::all`].
2100        let empty: Vec<CheckedCondition> = vec![];
2101        assert!(
2102            CheckedCondition::all_satisfied(&empty),
2103            "empty slice must fold to vacuous truth matching `[T]::iter().all(_)` — a regression that clamped the empty corner to false would silently block every no-boundary Process from advancing"
2104        );
2105    }
2106
2107    #[test]
2108    fn checked_condition_all_satisfied_matches_pre_lift_iter_all_chain_shape() {
2109        // Byte-identity pin against the pre-lift `.iter().all(|c|
2110        // c.satisfied)` chain shape the reconciler's two boundary
2111        // gates hand-authored. Sweeps every corner every gate
2112        // plausibly encounters (empty slice, single satisfied,
2113        // single unsatisfied, mixed satisfied first, mixed
2114        // unsatisfied first) so a regression that reshaped either
2115        // link surfaces HERE rather than at the two downstream phase
2116        // gates.
2117        let corners: Vec<Vec<CheckedCondition>> = vec![
2118            vec![],
2119            vec![sample_checked(true)],
2120            vec![sample_checked(false)],
2121            vec![sample_checked(true), sample_checked(false)],
2122            vec![sample_checked(false), sample_checked(true)],
2123            vec![
2124                sample_checked(true),
2125                sample_checked(true),
2126                sample_checked(true),
2127            ],
2128            vec![
2129                sample_checked(false),
2130                sample_checked(false),
2131                sample_checked(false),
2132            ],
2133        ];
2134        for corner in &corners {
2135            let via_primitive = CheckedCondition::all_satisfied(corner);
2136            #[allow(clippy::redundant_closure_for_method_calls)]
2137            let hand_authored = corner.iter().all(|c| c.satisfied);
2138            assert_eq!(
2139                via_primitive, hand_authored,
2140                "all_satisfied fold must match hand-authored .iter().all(|c| c.satisfied) chain byte-identically at corner {corner:?}"
2141            );
2142        }
2143    }
2144
2145    #[test]
2146    fn checked_condition_all_satisfied_short_circuits_on_first_unsatisfied_row() {
2147        // Semantic pin against [`Iterator::all`]'s short-circuit
2148        // discipline: a regression that folded via `checked.iter()
2149        // .filter(|c| c.satisfied).count() == checked.len()` would
2150        // still produce the same `bool` result but would eagerly
2151        // walk every row, and a future addition of an expensive
2152        // per-row side effect (a metric emit, a log line, a
2153        // conditional postcondition-retry hook) would silently fire
2154        // on every row past the first failure. The primitive must
2155        // preserve the pre-lift short-circuit — a regression that
2156        // dropped it would drift telemetry, not correctness, and
2157        // would evade every other pin here. This test verifies
2158        // short-circuit by threading a counter through a peer
2159        // predicate that mirrors [`CheckedCondition::satisfied`]'s
2160        // read.
2161        use std::cell::Cell;
2162        let visited = Cell::new(0_usize);
2163        let checked: Vec<CheckedCondition> = vec![
2164            sample_checked(true),
2165            sample_checked(false),
2166            sample_checked(true),
2167            sample_checked(true),
2168        ];
2169        // Manual short-circuit fold that counts per-row reads —
2170        // must match `all_satisfied`'s count on the same slice.
2171        let via_manual = checked.iter().all(|c| {
2172            visited.set(visited.get() + 1);
2173            c.satisfied
2174        });
2175        let manual_visited = visited.get();
2176        visited.set(0);
2177        // Mirror the primitive's iteration by re-running the same
2178        // fold shape and confirming the visited count matches — the
2179        // primitive itself doesn't take a side-effecting closure,
2180        // but this pin confirms the semantic shape (2 visits on
2181        // this slice: row 0 satisfied, row 1 unsatisfied, stop).
2182        assert_eq!(via_manual, CheckedCondition::all_satisfied(&checked));
2183        assert_eq!(
2184            manual_visited, 2,
2185            "short-circuit must stop at the first unsatisfied row (index 1); manual fold visited {manual_visited} rows"
2186        );
2187    }
2188}