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::crd::Process;
10use crate::json_object::ValueGetExt;
11
12/// Standard K8s Condition (shape of `metav1.Condition`).
13#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
14#[serde(rename_all = "camelCase")]
15pub struct ProcessCondition {
16    #[serde(rename = "type")]
17    pub type_: String,
18    pub status: String,
19    pub last_transition_time: DateTime<Utc>,
20    #[serde(default, skip_serializing_if = "Option::is_none")]
21    pub reason: Option<String>,
22    #[serde(default, skip_serializing_if = "Option::is_none")]
23    pub message: Option<String>,
24}
25
26impl ProcessCondition {
27    pub fn ready(reason: impl Into<String>, message: Option<String>) -> Self {
28        Self {
29            type_: "Ready".into(),
30            status: "True".into(),
31            last_transition_time: Utc::now(),
32            reason: Some(reason.into()),
33            message,
34        }
35    }
36
37    pub fn not_ready(reason: impl Into<String>, message: impl Into<String>) -> Self {
38        Self {
39            type_: "Ready".into(),
40            status: "False".into(),
41            last_transition_time: Utc::now(),
42            reason: Some(reason.into()),
43            message: Some(message.into()),
44        }
45    }
46
47    pub fn attested(root: &str) -> Self {
48        Self {
49            type_: "Attested".into(),
50            status: "True".into(),
51            last_transition_time: Utc::now(),
52            reason: Some("AttestationWritten".into()),
53            message: Some(format!("composed_root={root}")),
54        }
55    }
56}
57
58/// Reference to a FluxCD resource emitted as part of this Process.
59#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
60#[serde(rename_all = "camelCase")]
61pub struct FluxResourceRef {
62    pub api_version: String,
63    pub kind: String,
64    pub name: String,
65    pub namespace: String,
66    #[serde(default)]
67    pub ready: bool,
68    #[serde(default, skip_serializing_if = "Option::is_none")]
69    pub message: Option<String>,
70    #[serde(default, skip_serializing_if = "Option::is_none")]
71    pub last_check: Option<DateTime<Utc>>,
72}
73
74impl FluxResourceRef {
75    /// Pure typed projection of the four fetch coordinates
76    /// `(namespace, api_version, kind, name)` every consumer that
77    /// dispatches this persisted reference through kube-rs's dynamic-
78    /// object surface splats by hand pre-lift. The 4-tuple binds the
79    /// slot order at ONE typed accessor so a copy-paste at any downstream
80    /// consumer cannot swap two adjacent `&str` slots in the fetch call.
81    ///
82    /// Peer projection to
83    /// [`crate::k8s_wire_identity::K8sWireIdentity`] on the static-
84    /// identity axis: [`K8sWireIdentity`] carries a
85    /// `(&'static str, &'static str)` closed-set variant's pair for
86    /// emit-time (RENDER phase) composition; this method carries the
87    /// full `(ns, apiVersion, kind, name)` 4-slot borrow for fetch-time
88    /// (VERIFY / ATTEST-heartbeat) composition where the ref's payload
89    /// comes back off the persisted `ProcessStatus.flux_resources`
90    /// slice with owned `String`s rather than static literals. The two
91    /// primitives partition the fetch axis by whether the caller starts
92    /// from a closed-set variant (emit-time) or a persisted status
93    /// slice (fetch-time).
94    ///
95    /// Pre-lift the 5-slot `ssapply::fetch(client, &r.namespace,
96    /// &r.api_version, &r.kind, &r.name)` splat was hand-authored at
97    /// TWO sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold
98    /// in `tatara-reconciler::phase_machine`:
99    /// * `handle_running` — the VERIFY-phase per-ref readiness probe
100    ///   that populates the updated `FluxResourceRef` slice with
101    ///   `ready` + `message` + `last_check`.
102    /// * `handle_attested` — the ATTEST-heartbeat drift detector that
103    ///   short-circuits on the first non-Ready ref.
104    ///
105    /// Both sites splatted the SAME four `&r.X` field borrows in the
106    /// SAME order into raw `ssapply::fetch`. A copy-paste that swapped
107    /// two adjacent `&str` slots (`&r.api_version` and `&r.kind` are
108    /// both strings that look interchangeable to a mechanical
109    /// substitution) would silently 404 at wire time and diagnose as a
110    /// broken CRD rather than as slot skew at the callsite. Post-lift
111    /// each site names the ref ONCE and unpacks it through this ONE
112    /// projection; the slot order binds structurally at the tuple
113    /// return so a caller cannot desync one axis.
114    ///
115    /// A future addition (a case-fold normalization on the group, a
116    /// virtual-cluster prefix rewrite for multi-tenancy, a
117    /// `generateName` fallback on the name slot, a cluster-cache
118    /// short-circuit inserted between the projection and the fetch
119    /// call) lands at this ONE method and every downstream fetch
120    /// consumer inherits the upgrade mechanically — no per-site edit
121    /// at `handle_running` / `handle_attested` / any future kenshi-
122    /// runner / mirror-audit / drift-probe consumer that grows a third
123    /// consumer.
124    ///
125    /// Return-order pin lives at
126    /// [`tests::flux_resource_ref_fetch_coords_binds_slots_by_position`]
127    /// so a regression that swapped `namespace` and `api_version`
128    /// (both `String`, same type) inside the tuple constructor fails-
129    /// loudly here rather than as a silent wire-time 404 at every
130    /// downstream fetch consumer.
131    ///
132    /// Theory grounding: THEORY.md §II.1 invariant 5 (composition
133    /// preserves proofs — the 4-tuple slot order binds at ONE typed
134    /// projection so a regression across the two fields of the same
135    /// `String` type fails at the projection's positional pin rather
136    /// than at every downstream fetch consumer). THEORY.md §VI.1
137    /// (generation over composition — the 5-slot splat recurred at
138    /// two hand-authored sites past the ≥ 2 duplication trigger, and
139    /// is lifted to ONE typed borrow-projection here).
140    pub fn fetch_coords(&self) -> (&str, &str, &str, &str) {
141        (&self.namespace, &self.api_version, &self.kind, &self.name)
142    }
143
144    /// Compose a `FluxResourceRef` stamped at "observed now" — the
145    /// `last_check` slot is set to `Some(Utc::now())` at ONE substrate
146    /// owner, and the four coordinate slots + `ready` + `message`
147    /// are bound positionally so a slot-swap regression surfaces at
148    /// the constructor's positional pin rather than as silent drift
149    /// at every downstream `ProcessStatus.flux_resources` writer.
150    ///
151    /// Pre-lift the 7-slot `FluxResourceRef { …, last_check:
152    /// Some(chrono::Utc::now()) }` struct-literal was hand-authored
153    /// at TWO sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
154    /// threshold in `tatara-reconciler::phase_machine`:
155    /// * `handle_running` — the VERIFY-phase per-ref rebuild that
156    ///   restamps each polled ref with fresh `ready` + `message` +
157    ///   `last_check`.
158    /// * `flux_ref_from_json` — the post-SSA initial-state seeder
159    ///   that stamps a freshly-applied ref as `ready = false`,
160    ///   `message = Some("applied; awaiting reconciliation")`,
161    ///   `last_check = Some(Utc::now())`.
162    ///
163    /// Both sites restated the SAME seven field bindings in the
164    /// SAME order, and both restated the SAME `Some(chrono::Utc::
165    /// now())` stamp. A copy-paste that swapped two adjacent
166    /// `String` slots (`api_version` and `kind`, `kind` and `name`,
167    /// or `name` and `namespace` are all mechanically
168    /// indistinguishable at the type level) would silently persist
169    /// a slot-inverted ref that the downstream Flux fetch consumer
170    /// (via [`Self::fetch_coords`]) would then 404 on. Post-lift
171    /// both sites name the six inputs ONCE and route through this
172    /// ONE composer; the seventh slot (`last_check`) is stamped at
173    /// the composer's body so a future injection point (a fake
174    /// clock for testing, a monotonic-clock cross-check, a per-
175    /// fleet skew tolerance) lands at ONE substrate site rather
176    /// than at every hand-authored `Some(chrono::Utc::now())` stamp.
177    ///
178    /// Return-order pin lives at
179    /// [`tests::flux_resource_ref_observed_binds_slots_by_position`]
180    /// so a regression that swapped `api_version` and `kind` (both
181    /// `String`, same type) inside the constructor's argument list
182    /// fails-loudly here rather than as a silent wire-time 404 at
183    /// every downstream fetch consumer.
184    ///
185    /// Theory grounding: THEORY.md §II.1 invariant 5 (composition
186    /// preserves proofs — the 6-slot positional binding + the
187    /// `last_check` stamp compose at ONE typed owner, so a
188    /// regression across the four `String` coordinate slots fails
189    /// at the composer's positional pin rather than at every
190    /// downstream Flux status writer). THEORY.md §VI.1 (generation
191    /// over composition — the 7-slot struct-literal recurred at two
192    /// hand-authored sites past the ≥ 2 duplication trigger, and is
193    /// lifted to ONE typed composer here).
194    pub fn observed(
195        api_version: String,
196        kind: String,
197        name: String,
198        namespace: String,
199        ready: bool,
200        message: Option<String>,
201    ) -> Self {
202        Self {
203            api_version,
204            kind,
205            name,
206            namespace,
207            ready,
208            message,
209            last_check: Some(Utc::now()),
210        }
211    }
212
213    /// Compose a `FluxResourceRef` in the pre-observation shape — the
214    /// 4-slot coordinate binding with the three status slots defaulted
215    /// (`ready: false`, `message: None`, `last_check: None`). The
216    /// deterministic-fixture peer of [`Self::observed`] on the same
217    /// `→ FluxResourceRef` composer axis: `observed` reads the wall
218    /// clock and takes 6 args (a live post-fetch stamp), `pending`
219    /// reads no clock and takes 4 args (a pre-observation fixture
220    /// seed, and the natural base for `..base.clone()` spread updates
221    /// that vary a single slot for a per-corner test sweep).
222    ///
223    /// Pre-lift the SAME 7-slot `FluxResourceRef { api_version, kind,
224    /// name, namespace, ready: false, message: None, last_check: None
225    /// }` struct-literal was hand-authored at THREE workspace-wide
226    /// fixture sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
227    /// threshold:
228    ///
229    /// * [`crate::crd`]
230    ///   `crd::observed_flux_resources_tests::sample_flux_ref(name)`
231    ///   — the shared `Kustomization`/`flux-system` fixture the
232    ///   `Process::observed_flux_resources` pin family destructures
233    ///   for its `flux_resources`-populated corners.
234    /// * `tatara-reconciler::ssapply::tests::sample_flux_ref_for_diag`
235    ///   — the `HelmRelease`/`flux-system` fixture the
236    ///   `flux_ref_fetch_error_context` diagnostic-wording pin
237    ///   family destructures for its (kind, name) slot-coverage
238    ///   sweep.
239    /// * `tatara-reconciler::ssapply::tests::
240    ///   flux_ref_fetch_error_context_matches_pre_lift_hand_authored_wording`
241    ///   — the inline 7-slot literal inside the cross-substrate
242    ///   coherence pin's per-case sweep over three distinct
243    ///   `(api_version, kind, name, namespace)` tuples.
244    ///
245    /// All THREE sites restated the SAME seven field bindings in the
246    /// SAME order and the SAME three defaulted status slots (`ready:
247    /// false, message: None, last_check: None`), differing only in
248    /// the four coordinate `String` values. Post-lift each callsite
249    /// reads `FluxResourceRef::pending(<api_version>, <kind>, <name>,
250    /// <namespace>)` and the four-slot bind + three-slot default
251    /// sinks live at ONE substrate owner.
252    ///
253    /// The `impl Into<String>` signature accepts BOTH `&'static str`
254    /// (the fixture-helper sites that spell coordinate literals
255    /// inline) AND owned `String` (a future callsite handing off a
256    /// dynamically-derived coordinate) without widening. Matches the
257    /// discipline of the sibling substrate composers
258    /// [`crate::pool::PoolMember::unallocated`] +
259    /// [`crate::allocation::AllocationRef::new`] on the identity-slot
260    /// axis.
261    ///
262    /// A future normalization (a case-fold on the group, a
263    /// virtual-cluster prefix rewrite for multi-tenancy, a stricter
264    /// kind gate, a `generateName` fallback on the name slot, a
265    /// canonical rename of one of the three defaulted status slots
266    /// to a typed `PreObservation` marker) lands at THIS ONE
267    /// substrate primitive and every downstream fixture / helper
268    /// inherits the upgrade mechanically — no per-site edit at any
269    /// of the THREE listed callers or at future consumers (a
270    /// stable-name claim-arbiter's pending-ref seed, a kenshi-runner
271    /// pre-observation fixture, a mirror-audit drift-probe test
272    /// helper).
273    ///
274    /// Theory grounding: THEORY.md §II.1 invariant 5 (composition
275    /// preserves proofs — the 4-slot positional binding + the three
276    /// defaulted status slots compose at ONE typed owner, so a
277    /// regression across the four `String` coordinate slots fails at
278    /// the composer's positional pin rather than at every downstream
279    /// fixture consumer). THEORY.md §VI.1 (generation over
280    /// composition — the 7-slot struct-literal recurred at three
281    /// hand-authored fixture sites past the ≥ 2 duplication trigger,
282    /// and is lifted to ONE typed composer here).
283    #[must_use]
284    pub fn pending(
285        api_version: impl Into<String>,
286        kind: impl Into<String>,
287        name: impl Into<String>,
288        namespace: impl Into<String>,
289    ) -> Self {
290        Self {
291            api_version: api_version.into(),
292            kind: kind.into(),
293            name: name.into(),
294            namespace: namespace.into(),
295            ready: false,
296            message: None,
297            last_check: None,
298        }
299    }
300}
301
302/// Identifying coordinates of a rendered K8s resource — the
303/// `(apiVersion, kind, metadata.name, metadata.namespace)` 4-tuple
304/// every consumer that walks a rendered `serde_json::Value` resource
305/// unwraps by hand pre-lift.
306///
307/// The three K8s API-path segments (`apiVersion`, `kind`,
308/// `metadata.name`) are REQUIRED — a rendered resource missing any
309/// of them cannot be applied via kube-rs's dynamic API surface, so
310/// the extraction fails fast at the boundary rather than as a
311/// downstream `Api::patch` panic. `metadata.namespace` is
312/// intentionally kept as `Option<String>` because different consumers
313/// resolve the fallback differently: `apply_owned` uses the
314/// caller-supplied `namespace: &str` argument (the reconciler already
315/// resolved the target namespace upstream), while `flux_ref_from_json`
316/// records the K8s canonical `"default"` fallback into the persisted
317/// `FluxResourceRef.namespace` slot. The peer method
318/// [`Self::namespace_or_default`] applies the K8s canonical fallback
319/// (`Process::DEFAULT_NAMESPACE = "default"`) for consumers wanting
320/// the same shape [`FluxResourceRef.namespace`] carries.
321///
322/// Pre-lift the 3+1 slot extraction was hand-authored at TWO sites
323/// past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold across
324/// `tatara-reconciler`:
325/// * `tatara-reconciler::phase_machine::flux_ref_from_json` — the
326///   post-SSA `FluxResourceRef` builder that persists into
327///   `ProcessStatus.flux_resources`; namespace half fallback-
328///   defaulted to `"default"`.
329/// * `tatara-reconciler::ssapply::apply_owned` — the SSA entry
330///   point that extracts (apiVersion, kind, name) for the
331///   [`kube::Api::patch`] call; namespace half discarded (the
332///   `namespace: &str` argument comes from the caller upstream).
333///
334/// Both callsites restated the same three
335/// `.get(K).and_then(|v| v.as_str()).ok_or_else(|| anyhow!(...))?
336/// .to_string()` incantations with subtly different error wording
337/// (`"resource missing X"` vs `"rendered resource missing X"`); post-
338/// lift both route through this ONE substrate owner with the
339/// canonical `"rendered resource missing X"` wording. A future
340/// addition (case-fold on the group, a rename of the namespace
341/// fallback, a stricter kind gate, a Unicode-safe collation step,
342/// support for `metadata.generateName` as a name fallback) lands at
343/// the primitive's body on the substrate, not at 2 independent
344/// hand-writes across 2 reconciler files.
345///
346/// Namespace fallback const is shared with
347/// [`Process::DEFAULT_NAMESPACE`] — a rename of the K8s canonical
348/// default namespace lands at that ONE workspace-wide const, not at
349/// per-primitive local literals that would drift silently.
350#[derive(Clone, Debug, PartialEq, Eq)]
351pub struct RenderedResourceCoords {
352    /// `apiVersion` — the group+version pair kube-rs uses to resolve
353    /// the `ApiResource` for the SSA call.
354    pub api_version: String,
355    /// `kind` — the resource kind (Kustomization, HelmRelease, …).
356    pub kind: String,
357    /// `metadata.name` — the API-path leaf segment.
358    pub name: String,
359    /// `metadata.namespace` — raw from the resource, `None` when the
360    /// slot is absent (a cluster-scoped resource, or a namespaced
361    /// resource whose namespace was left for the API server to
362    /// substitute). Consumers apply their own fallback:
363    /// [`Self::namespace_or_default`] applies the K8s canonical
364    /// `"default"` (matching what [`FluxResourceRef.namespace`]
365    /// records); other consumers substitute a caller-supplied string
366    /// (see `tatara-reconciler::ssapply::apply_owned`).
367    pub namespace: Option<String>,
368}
369
370impl RenderedResourceCoords {
371    /// Extract the 4-tuple from a rendered K8s resource JSON `Value`.
372    ///
373    /// Fails with a canonical `"rendered resource missing X"` message
374    /// when any of the three required slots (`apiVersion`, `kind`,
375    /// `metadata.name`) is absent or non-string; `metadata.namespace`
376    /// is optional and captured as `None` when absent.
377    ///
378    /// The error wording is pinned by
379    /// [`tests::rendered_resource_coords_error_wording_is_canonical`]
380    /// so a regression that reshaped the message surfaces at the test
381    /// surface rather than as silent drift between the two pre-lift
382    /// call sites (which used subtly different wording — `"resource
383    /// missing X"` in `apply_owned` vs `"rendered resource missing
384    /// X"` in `flux_ref_from_json`).
385    pub fn from_json(res: &Value) -> anyhow::Result<Self> {
386        // Four `.get(<key>).and_then(|v| v.as_str())` READ chains
387        // (`apiVersion`, `kind`, `metadata.name`, `metadata.namespace`)
388        // now route through the ONE substrate primitive
389        // `crate::json_object::ValueGetExt::get_str` — the string-axis
390        // sibling of `get_i64` on the same
391        // `.get(<key>).and_then(|v| v.as_<T>())` READ-chain axis-family.
392        // A future normalization (Unicode NFC-fold, whitespace trim,
393        // empty-string rejection) lands at the substrate primitive and
394        // every downstream `apiVersion` / `kind` /
395        // `metadata.{name,namespace}` reader inherits it mechanically.
396        let api_version = res
397            .get_str("apiVersion")
398            .ok_or_else(|| anyhow::anyhow!("rendered resource missing apiVersion"))?
399            .to_string();
400        let kind = res
401            .get_str("kind")
402            .ok_or_else(|| anyhow::anyhow!("rendered resource missing kind"))?
403            .to_string();
404        let metadata = res.get("metadata");
405        let name = metadata
406            .and_then(|m| m.get_str("name"))
407            .ok_or_else(|| anyhow::anyhow!("rendered resource missing metadata.name"))?
408            .to_string();
409        let namespace = metadata
410            .and_then(|m| m.get_str("namespace"))
411            .map(str::to_string);
412        Ok(Self {
413            api_version,
414            kind,
415            name,
416            namespace,
417        })
418    }
419
420    /// `metadata.namespace` slice with the K8s canonical `"default"`
421    /// fallback applied — matching what [`Process::DEFAULT_NAMESPACE`]
422    /// spells for the `Process`-borne coordinate primitive family
423    /// and what [`FluxResourceRef.namespace`] records into
424    /// `ProcessStatus.flux_resources`.
425    pub fn namespace_or_default(&self) -> &str {
426        self.namespace
427            .as_deref()
428            .unwrap_or(Process::DEFAULT_NAMESPACE)
429    }
430}
431
432/// A boundary condition paired with its current satisfaction state.
433#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
434#[serde(rename_all = "camelCase")]
435pub struct CheckedCondition {
436    #[serde(flatten)]
437    pub condition: Condition,
438    pub satisfied: bool,
439    #[serde(default, skip_serializing_if = "Option::is_none")]
440    pub last_check: Option<DateTime<Utc>>,
441    #[serde(default, skip_serializing_if = "Option::is_none")]
442    pub message: Option<String>,
443}
444
445impl CheckedCondition {
446    /// True iff every [`CheckedCondition`] in the slice has
447    /// `satisfied == true` — the ONE-line collapse of the paired
448    /// `checked.iter().all(|c| c.satisfied)` incantation the
449    /// reconciler's precondition + postcondition boundary gates both
450    /// spelled by hand pre-lift.
451    ///
452    /// Pre-lift the SAME `.iter().all(|c| c.satisfied)` chain was
453    /// hand-authored at TWO sites past the ★★ PRIME-DIRECTIVE ≥ 2
454    /// duplication threshold in `tatara-reconciler::phase_machine`,
455    /// each walking the SAME `Vec<CheckedCondition>` → `bool`
456    /// projection to gate a phase transition on a boundary predicate:
457    /// * `handle_execing` — the PROVE-phase precondition gate that
458    ///   stays in Execing (heartbeat requeue) while any precondition
459    ///   remains unsatisfied and proceeds to RENDER only when every
460    ///   precondition holds.
461    /// * `handle_running` — the VERIFY-phase postcondition gate that
462    ///   stays in Running (heartbeat requeue) while any postcondition
463    ///   remains unsatisfied and advances to Attested only when every
464    ///   postcondition holds.
465    ///
466    /// Both sites walked the SAME `Iterator::all` short-circuit on the
467    /// SAME `bool` slot of the SAME struct. Post-lift both consumers
468    /// name the slice ONCE and route through this ONE primitive; the
469    /// vacuous-truth corner (empty slice → `true`, matching
470    /// [`Iterator::all`]'s empty-input identity) sits at ONE substrate
471    /// site so a future normalization (a per-slot weight overlay, a
472    /// per-kind override that treats `Warn`-severity failures as
473    /// satisfied, a compliance-baseline gate that requires N-of-M
474    /// rather than all-of-M) lands at ONE substrate function and both
475    /// downstream phase gates inherit the upgrade mechanically.
476    ///
477    /// Return-form axis: `bool` — the exact type each phase gate
478    /// pre-lift bound at `let all_pass = <chain>;` and immediately
479    /// consumed in a `!all_pass` short-circuit + a `message` slot's
480    /// ternary branch. The `&[Self]` argument accepts every pre-lift
481    /// slice provenance verbatim: a `&Vec<CheckedCondition>` (both
482    /// pre-lift sites had the `Vec` on the stack from
483    /// [`crate::phase_machine::evaluate_conditions`]'s owned return)
484    /// coerces through auto-deref, so no callsite has to change its
485    /// upstream provenance to route through the primitive.
486    ///
487    /// Peer to the sibling projection [`Self::satisfied`] on the (row
488    /// scope × predicate) axis pair: `satisfied` is the per-row
489    /// projection; `all_satisfied` is the slice-wide fold of the same
490    /// bit. Both live on `CheckedCondition` so a future rename or
491    /// per-slot normalization travels through the same owner without
492    /// splitting between "per-row" and "slice-wide" call sinks.
493    ///
494    /// Return-shape pin lives at
495    /// [`tests::checked_condition_all_satisfied_matches_pre_lift_iter_all_chain_shape`]
496    /// so a regression that flipped the fold direction (`any` for
497    /// `all`), inverted the bit (`!c.satisfied`), or reshaped the
498    /// return form (an owned `Vec<bool>` instead of the folded `bool`)
499    /// fails-loudly here rather than as silent operator-facing skew
500    /// between the pre-lift `if !all_pass { requeue }` gate and the
501    /// post-lift call — every downstream consumer would still
502    /// short-circuit but on inverted semantics.
503    ///
504    /// Theory grounding: THEORY.md §VI.1 (generation over composition
505    /// — the 1-line `.iter().all(...)` chain recurred at two hand-
506    /// authored sites past the ≥ 2 duplication trigger, and is lifted
507    /// to ONE typed fold here). THEORY.md §II.1 invariant 5
508    /// (composition preserves proofs — the empty-slice vacuous-truth
509    /// corner + the fold direction + the projected bit's polarity all
510    /// bind at ONE substrate site, so a regression across any of the
511    /// three surfaces at [`tests::checked_condition_all_satisfied_*`]
512    /// pin rather than as silent gate-flip at every downstream phase
513    /// handler).
514    #[must_use]
515    pub fn all_satisfied(checked: &[Self]) -> bool {
516        checked.iter().all(|c| c.satisfied)
517    }
518}
519
520/// Summary of boundary verification.
521#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
522#[serde(rename_all = "camelCase")]
523pub struct BoundaryStatus {
524    #[serde(default)]
525    pub preconditions: Vec<CheckedCondition>,
526    #[serde(default)]
527    pub postconditions: Vec<CheckedCondition>,
528    /// Absolute deadline for VERIFY (derived from `spec.boundary.timeout`).
529    #[serde(default, skip_serializing_if = "Option::is_none")]
530    pub deadline: Option<DateTime<Utc>>,
531}
532
533/// Summary of compliance checks at the latest attestation.
534#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
535#[serde(rename_all = "camelCase")]
536pub struct ComplianceStatus {
537    #[serde(default, skip_serializing_if = "Option::is_none")]
538    pub baseline: Option<String>,
539    pub satisfied: u32,
540    pub violated: u32,
541    pub total: u32,
542    #[serde(default)]
543    pub violations: Vec<String>,
544}
545
546#[cfg(test)]
547mod tests {
548    use super::*;
549    use serde_json::json;
550
551    // ─── RenderedResourceCoords substrate pins ──────────────────────
552
553    #[test]
554    fn rendered_resource_coords_from_json_extracts_all_four_slots_when_present() {
555        let res = json!({
556            "apiVersion": "kustomize.toolkit.fluxcd.io/v1",
557            "kind": "Kustomization",
558            "metadata": {
559                "name": "observability-stack",
560                "namespace": "flux-system",
561            },
562        });
563        let c = RenderedResourceCoords::from_json(&res).expect("extract");
564        assert_eq!(c.api_version, "kustomize.toolkit.fluxcd.io/v1");
565        assert_eq!(c.kind, "Kustomization");
566        assert_eq!(c.name, "observability-stack");
567        assert_eq!(c.namespace.as_deref(), Some("flux-system"));
568    }
569
570    #[test]
571    fn rendered_resource_coords_from_json_captures_absent_namespace_as_none() {
572        // Cluster-scoped resource — `metadata.namespace` intentionally absent.
573        let res = json!({
574            "apiVersion": "v1",
575            "kind": "Namespace",
576            "metadata": {"name": "demo-test"},
577        });
578        let c = RenderedResourceCoords::from_json(&res).expect("extract");
579        assert_eq!(c.namespace, None);
580        assert_eq!(c.name, "demo-test");
581    }
582
583    #[test]
584    fn rendered_resource_coords_from_json_errors_on_missing_api_version() {
585        let res = json!({"kind": "K", "metadata": {"name": "n"}});
586        let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
587        assert_eq!(e.to_string(), "rendered resource missing apiVersion");
588    }
589
590    #[test]
591    fn rendered_resource_coords_from_json_errors_on_missing_kind() {
592        let res = json!({"apiVersion": "v1", "metadata": {"name": "n"}});
593        let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
594        assert_eq!(e.to_string(), "rendered resource missing kind");
595    }
596
597    #[test]
598    fn rendered_resource_coords_from_json_errors_on_missing_metadata_name() {
599        let res = json!({"apiVersion": "v1", "kind": "K", "metadata": {}});
600        let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
601        assert_eq!(e.to_string(), "rendered resource missing metadata.name");
602    }
603
604    #[test]
605    fn rendered_resource_coords_from_json_errors_on_missing_metadata_object() {
606        // `metadata` absent entirely — same failure as `metadata.name` missing,
607        // because the API-path leaf segment cannot be resolved.
608        let res = json!({"apiVersion": "v1", "kind": "K"});
609        let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
610        assert_eq!(e.to_string(), "rendered resource missing metadata.name");
611    }
612
613    #[test]
614    fn rendered_resource_coords_from_json_errors_on_non_string_slot() {
615        // A numeric `apiVersion` slot falls through the `.as_str()` gate and
616        // triggers the same missing-slot failure as absence — the API-path
617        // segment is not a string.
618        let res = json!({
619            "apiVersion": 42,
620            "kind": "K",
621            "metadata": {"name": "n"},
622        });
623        let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
624        assert_eq!(e.to_string(), "rendered resource missing apiVersion");
625    }
626
627    #[test]
628    fn rendered_resource_coords_error_wording_is_canonical() {
629        // Pins the exact spelling every downstream consumer sees.
630        // Pre-lift wording differed across the two call sites (`"resource
631        // missing X"` in `apply_owned` vs `"rendered resource missing X"` in
632        // `flux_ref_from_json`); post-lift the canonical wording is
633        // `"rendered resource missing X"` at every site.
634        let cases = [
635            (
636                "apiVersion",
637                json!({"kind": "K", "metadata": {"name": "n"}}),
638            ),
639            (
640                "kind",
641                json!({"apiVersion": "v1", "metadata": {"name": "n"}}),
642            ),
643            (
644                "metadata.name",
645                json!({"apiVersion": "v1", "kind": "K", "metadata": {}}),
646            ),
647        ];
648        for (slot, res) in cases {
649            let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
650            assert_eq!(
651                e.to_string(),
652                format!("rendered resource missing {slot}"),
653                "slot {slot} error must be canonical"
654            );
655        }
656    }
657
658    #[test]
659    fn rendered_resource_coords_namespace_or_default_returns_slice_when_some() {
660        let c = RenderedResourceCoords {
661            api_version: "v1".into(),
662            kind: "K".into(),
663            name: "n".into(),
664            namespace: Some("prod".into()),
665        };
666        assert_eq!(c.namespace_or_default(), "prod");
667    }
668
669    #[test]
670    fn rendered_resource_coords_namespace_or_default_falls_back_when_none() {
671        let c = RenderedResourceCoords {
672            api_version: "v1".into(),
673            kind: "K".into(),
674            name: "n".into(),
675            namespace: None,
676        };
677        assert_eq!(c.namespace_or_default(), Process::DEFAULT_NAMESPACE);
678        assert_eq!(c.namespace_or_default(), "default");
679    }
680
681    // ─── FluxResourceRef::fetch_coords substrate pins ─────────────
682    //
683    // The 4-slot `(&namespace, &api_version, &kind, &name)` borrow
684    // projection lifts the pre-existing 5-slot `ssapply::fetch(client,
685    // &r.namespace, &r.api_version, &r.kind, &r.name)` splat that
686    // recurred at TWO hand-authored sites in
687    // `tatara-reconciler::phase_machine` (`handle_running`,
688    // `handle_attested`) past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
689    // trigger. These pins bind the slot order at fail-before-pass-
690    // after granularity so a regression that swapped `namespace` and
691    // `api_version` (both `String`, mechanically interchangeable to
692    // a bad refactor) surfaces HERE rather than as a silent wire-time
693    // 404 at every downstream Flux fetch consumer.
694
695    fn sample_flux_ref() -> FluxResourceRef {
696        // Slot values are deliberately distinct so a swap between any
697        // two adjacent tuple positions surfaces as an equality
698        // failure at the assertion site — a slot-inversion regression
699        // cannot masquerade as identity by accident.
700        FluxResourceRef {
701            api_version: "kustomize.toolkit.fluxcd.io/v1".to_string(),
702            kind: "Kustomization".to_string(),
703            name: "observability-stack".to_string(),
704            namespace: "flux-system".to_string(),
705            ready: true,
706            message: None,
707            last_check: None,
708        }
709    }
710
711    #[test]
712    fn flux_resource_ref_fetch_coords_binds_slots_by_position() {
713        // Positional pin: the 4-tuple return binds
714        // `(namespace, api_version, kind, name)` in THAT order,
715        // matching the raw `ssapply::fetch(client, ns, av, kind,
716        // name)` positional signature every pre-lift callsite splatted
717        // into. A regression that swapped ANY pair of adjacent slots
718        // (all four axes are `String` and mechanically
719        // indistinguishable at the type level) would surface here
720        // rather than as an operator-visible wire-form 404 at every
721        // downstream fetch consumer.
722        let r = sample_flux_ref();
723        let (ns, av, kind, name) = r.fetch_coords();
724        assert_eq!(ns, "flux-system", "position 0 must be namespace");
725        assert_eq!(
726            av, "kustomize.toolkit.fluxcd.io/v1",
727            "position 1 must be api_version"
728        );
729        assert_eq!(kind, "Kustomization", "position 2 must be kind");
730        assert_eq!(name, "observability-stack", "position 3 must be name");
731    }
732
733    #[test]
734    fn flux_resource_ref_fetch_coords_returns_borrows_of_owned_slots() {
735        // Borrow-discipline pin: the 4-tuple returns `&str` borrows
736        // of the enclosing `FluxResourceRef`'s owned `String` slots —
737        // NOT a fresh allocation or a clone. A regression that
738        // switched the projection to owned strings (via `.clone()` or
739        // `format!`) would defeat the zero-copy contract and would
740        // surface here via pointer-identity comparison.
741        let r = sample_flux_ref();
742        let (ns, av, kind, name) = r.fetch_coords();
743        assert!(std::ptr::eq(ns.as_ptr(), r.namespace.as_ptr()));
744        assert!(std::ptr::eq(av.as_ptr(), r.api_version.as_ptr()));
745        assert!(std::ptr::eq(kind.as_ptr(), r.kind.as_ptr()));
746        assert!(std::ptr::eq(name.as_ptr(), r.name.as_ptr()));
747    }
748
749    #[test]
750    fn flux_resource_ref_fetch_coords_is_a_pure_borrow_projection() {
751        // Purity pin: calling the projection twice on the same ref
752        // returns byte-identical slices (same pointer, same length).
753        // A regression that introduced state — a lazy-cached slot
754        // computed on first call, a normalization step that ran once
755        // and cached — would surface here rather than as silent drift
756        // between the VERIFY-phase and ATTEST-heartbeat consumers on
757        // the SAME ref within one reconcile pass.
758        let r = sample_flux_ref();
759        let a = r.fetch_coords();
760        let b = r.fetch_coords();
761        assert!(std::ptr::eq(a.0.as_ptr(), b.0.as_ptr()));
762        assert!(std::ptr::eq(a.1.as_ptr(), b.1.as_ptr()));
763        assert!(std::ptr::eq(a.2.as_ptr(), b.2.as_ptr()));
764        assert!(std::ptr::eq(a.3.as_ptr(), b.3.as_ptr()));
765    }
766
767    #[test]
768    fn flux_resource_ref_fetch_coords_ignores_status_slots() {
769        // Coverage pin: the projection exposes ONLY the four API-path
770        // slots the fetch call requires; the ref's status slots
771        // (`ready`, `message`, `last_check`) are deliberately absent
772        // from the tuple. The fetch signature admits four `&str`
773        // slots, and the projection carries EXACTLY those four — no
774        // silent widening that would surface as an arity mismatch at
775        // every downstream `fetch(...)` call.
776        let r = sample_flux_ref();
777        let coords = r.fetch_coords();
778        assert_eq!(
779            std::mem::size_of_val(&coords),
780            std::mem::size_of::<(&str, &str, &str, &str)>(),
781            "the 4-tuple width must match the raw fetch signature's four `&str` slots"
782        );
783    }
784
785    // ─── FluxResourceRef::observed substrate pins ─────────────────
786    //
787    // The 6-arg composer stamps `last_check` at ONE substrate site
788    // (the pre-lift 7-slot struct-literal restated `Some(chrono::
789    // Utc::now())` at TWO hand-authored sites in
790    // `tatara-reconciler::phase_machine` — `handle_running`'s per-
791    // ref VERIFY rebuild and `flux_ref_from_json`'s post-SSA
792    // seeder). These pins bind the six input slots by position so a
793    // regression that swapped `api_version` and `kind` (both
794    // `String`, mechanically interchangeable to a bad refactor)
795    // surfaces HERE rather than as a silent wire-time 404 at every
796    // downstream fetch consumer.
797    //
798    // Every test constructs distinct values across the four
799    // `String` coordinate slots so a slot swap fails structurally
800    // rather than by accident of matching literals.
801
802    #[test]
803    fn flux_resource_ref_observed_binds_slots_by_position() {
804        // Positional pin: the 6-arg constructor binds
805        // `(api_version, kind, name, namespace, ready, message)`
806        // in THAT order, matching the pre-lift 7-slot struct-
807        // literal's declaration order. A regression that swapped
808        // ANY pair of adjacent `String` coordinate slots (all four
809        // are mechanically indistinguishable at the type level)
810        // would surface here rather than as a wire-time 404 at
811        // every downstream Flux fetch consumer.
812        let r = FluxResourceRef::observed(
813            "kustomize.toolkit.fluxcd.io/v1".to_string(),
814            "Kustomization".to_string(),
815            "observability-stack".to_string(),
816            "flux-system".to_string(),
817            true,
818            Some("healthy".to_string()),
819        );
820        assert_eq!(r.api_version, "kustomize.toolkit.fluxcd.io/v1");
821        assert_eq!(r.kind, "Kustomization");
822        assert_eq!(r.name, "observability-stack");
823        assert_eq!(r.namespace, "flux-system");
824        assert!(r.ready);
825        assert_eq!(r.message.as_deref(), Some("healthy"));
826    }
827
828    #[test]
829    fn flux_resource_ref_observed_stamps_last_check_at_now() {
830        // Stamp pin: the `last_check` slot is filled with
831        // `Some(<recent Utc>)` at the composer's body. A
832        // regression that dropped the stamp (leaving `None`) or
833        // shifted it to a stale constant would surface here rather
834        // than as silent operator-observed staleness at
835        // `ProcessStatus.flux_resources` panels. Bounds the stamp
836        // to within a generous 5s window of the composer call so
837        // slow CI runners do not false-positive.
838        let before = Utc::now();
839        let r = FluxResourceRef::observed(
840            "v1".to_string(),
841            "K".to_string(),
842            "n".to_string(),
843            "ns".to_string(),
844            false,
845            None,
846        );
847        let after = Utc::now();
848        let stamp = r.last_check.expect("observed must stamp last_check");
849        assert!(stamp >= before, "stamp must be >= before-call `now`");
850        assert!(stamp <= after, "stamp must be <= after-call `now`");
851    }
852
853    #[test]
854    fn flux_resource_ref_observed_round_trips_through_fetch_coords() {
855        // Cross-composer coherence pin: a ref built by `observed`
856        // then unpacked by `fetch_coords` returns the same four
857        // slots in the peer projection's positional order
858        // `(namespace, api_version, kind, name)`. Composition of
859        // the two primitives on the same ref preserves the slot
860        // identity — a regression at either end (a slot swap in
861        // `observed`, or a slot swap in `fetch_coords`) would
862        // surface here rather than as silent drift between the
863        // writer and the reader on the same persisted slice.
864        let r = FluxResourceRef::observed(
865            "helm.toolkit.fluxcd.io/v2".to_string(),
866            "HelmRelease".to_string(),
867            "prometheus-op".to_string(),
868            "monitoring".to_string(),
869            false,
870            Some("applied; awaiting reconciliation".to_string()),
871        );
872        let (ns, av, kind, name) = r.fetch_coords();
873        assert_eq!(ns, "monitoring");
874        assert_eq!(av, "helm.toolkit.fluxcd.io/v2");
875        assert_eq!(kind, "HelmRelease");
876        assert_eq!(name, "prometheus-op");
877    }
878
879    #[test]
880    fn flux_resource_ref_observed_matches_pre_lift_struct_literal_field_for_field() {
881        // Byte-for-byte parity pin against the pre-lift 7-slot
882        // struct-literal spelled at BOTH `phase_machine::
883        // handle_running` and `phase_machine::flux_ref_from_json`.
884        // A regression that reordered any of the six inputs at
885        // the composer's argument list, or that swapped a
886        // `ready`/`message` pair inside the composer's body,
887        // would surface here rather than as silent divergence
888        // between the composer's output and the pre-lift hand-
889        // authored shape every persisted status writer restated.
890        let composed = FluxResourceRef::observed(
891            "source.toolkit.fluxcd.io/v1beta2".to_string(),
892            "OCIRepository".to_string(),
893            "chart-source".to_string(),
894            "flux-system".to_string(),
895            false,
896            Some("applied; awaiting reconciliation".to_string()),
897        );
898        // Hand-authored the same seven slots directly, with a
899        // held-open stamp window across the composer call.
900        let stamped = composed.last_check.expect("stamped");
901        let baseline = FluxResourceRef {
902            api_version: "source.toolkit.fluxcd.io/v1beta2".to_string(),
903            kind: "OCIRepository".to_string(),
904            name: "chart-source".to_string(),
905            namespace: "flux-system".to_string(),
906            ready: false,
907            message: Some("applied; awaiting reconciliation".to_string()),
908            last_check: Some(stamped),
909        };
910        assert_eq!(composed.api_version, baseline.api_version);
911        assert_eq!(composed.kind, baseline.kind);
912        assert_eq!(composed.name, baseline.name);
913        assert_eq!(composed.namespace, baseline.namespace);
914        assert_eq!(composed.ready, baseline.ready);
915        assert_eq!(composed.message, baseline.message);
916        assert_eq!(composed.last_check, baseline.last_check);
917    }
918
919    // ─── FluxResourceRef::pending substrate pins ─────────────────────
920    //
921    // Bind [`FluxResourceRef::pending`] at fail-before-pass-after
922    // granularity so a regression that leaked a non-default status
923    // slot (`ready: true`, `message: Some("something")`, `last_check:
924    // Some(Utc::now())`), swapped two adjacent coordinate slots (all
925    // four are `String` and mechanically interchangeable at the type
926    // level), or diverged from the pre-lift 7-slot struct-literal on
927    // any of the seven fields surfaces HERE rather than as silent
928    // operator-invisible drift at the 3 downstream fixture consumers
929    // (crd.rs `sample_flux_ref`, ssapply.rs `sample_flux_ref_for_diag`,
930    // ssapply.rs `flux_ref_fetch_error_context_matches_pre_lift_...`).
931    //
932    // Each pin is fail-before-pass-after: the primitive did not exist
933    // pre-lift, so any test that invokes it fails to compile pre-lift
934    // and passes post-lift; the byte-identity pins below then bind
935    // the specific shape choice.
936
937    #[test]
938    fn flux_resource_ref_pending_binds_coordinate_slots_by_position() {
939        // Positional pin: the 4-arg constructor binds `(api_version,
940        // kind, name, namespace)` in THAT order, matching the pre-
941        // lift 7-slot struct-literal's declaration order. A regression
942        // that swapped ANY pair of adjacent `String` coordinate slots
943        // (all four are mechanically indistinguishable at the type
944        // level) would surface here rather than as a wire-time 404 at
945        // every downstream Flux fetch consumer that walks
946        // `FluxResourceRef.fetch_coords`.
947        let r = FluxResourceRef::pending(
948            "kustomize.toolkit.fluxcd.io/v1",
949            "Kustomization",
950            "observability-stack",
951            "flux-system",
952        );
953        assert_eq!(r.api_version, "kustomize.toolkit.fluxcd.io/v1");
954        assert_eq!(r.kind, "Kustomization");
955        assert_eq!(r.name, "observability-stack");
956        assert_eq!(r.namespace, "flux-system");
957    }
958
959    #[test]
960    fn flux_resource_ref_pending_defaults_every_status_slot() {
961        // Default-slot pin: the three status slots (`ready`, `message`,
962        // `last_check`) are ALL defaulted at the composer's body — no
963        // wall-clock read, no non-`None` `message` leak, no `ready:
964        // true` regression that would silently un-pend the fixture.
965        // A regression that stamped `Some(Utc::now())` into
966        // `last_check` (matching the sibling `observed` composer's
967        // wall-clock read) would silently defeat the deterministic-
968        // fixture contract the peer partition holds.
969        let r = FluxResourceRef::pending("v1", "K", "n", "ns");
970        assert!(
971            !r.ready,
972            "pending composer must default `ready` to false — non-`false` breaks the pre-observation contract"
973        );
974        assert_eq!(
975            r.message, None,
976            "pending composer must default `message` to None — non-`None` leaks a stale message into the pre-observation seed",
977        );
978        assert_eq!(
979            r.last_check, None,
980            "pending composer must default `last_check` to None — a `Some(_)` leak defeats the deterministic-peer partition against `observed`",
981        );
982    }
983
984    #[test]
985    fn flux_resource_ref_pending_accepts_both_owned_and_borrowed_coordinates() {
986        // The `impl Into<String>` ergonomic contract: both `&'static
987        // str` literals (the fixture-helper sites that spell
988        // coordinates inline) and owned `String` (a future callsite
989        // handing off a dynamically-derived coordinate) round-trip
990        // through the SAME composer signature without widening. A
991        // regression that specialised the signature to one form or
992        // the other would break either the inline-literal helpers or
993        // the owned-`String` downstream consumers.
994        let borrowed: FluxResourceRef = FluxResourceRef::pending("v1", "K", "n", "ns");
995        let owned: FluxResourceRef = FluxResourceRef::pending(
996            "v1".to_string(),
997            "K".to_string(),
998            "n".to_string(),
999            "ns".to_string(),
1000        );
1001        assert_eq!(borrowed.api_version, owned.api_version);
1002        assert_eq!(borrowed.kind, owned.kind);
1003        assert_eq!(borrowed.name, owned.name);
1004        assert_eq!(borrowed.namespace, owned.namespace);
1005        assert_eq!(borrowed.ready, owned.ready);
1006        assert_eq!(borrowed.message, owned.message);
1007        assert_eq!(borrowed.last_check, owned.last_check);
1008    }
1009
1010    #[test]
1011    fn flux_resource_ref_pending_matches_pre_lift_struct_literal_bytewise() {
1012        // Byte-for-byte parity pin against the pre-lift 7-slot
1013        // struct-literal spelled at ALL THREE hand-authored fixture
1014        // sites (crd.rs `sample_flux_ref`, ssapply.rs
1015        // `sample_flux_ref_for_diag`, ssapply.rs inline in the
1016        // cross-substrate coherence pin's per-case sweep). Sweeps the
1017        // three representative coordinate tuples the pre-lift sites
1018        // used, so a regression that special-cased any one variant
1019        // (a `Kustomization`-only path via `if kind ==
1020        // "Kustomization" ...`) surfaces here.
1021        let cases = [
1022            (
1023                "kustomize.toolkit.fluxcd.io/v1",
1024                "Kustomization",
1025                "observability-stack",
1026                "flux-system",
1027            ),
1028            (
1029                "helm.toolkit.fluxcd.io/v2",
1030                "HelmRelease",
1031                "prometheus-op",
1032                "monitoring",
1033            ),
1034            (
1035                "source.toolkit.fluxcd.io/v1beta2",
1036                "OCIRepository",
1037                "chart-source",
1038                "flux-system",
1039            ),
1040        ];
1041        for (av, kind, name, ns) in cases {
1042            let composed = FluxResourceRef::pending(av, kind, name, ns);
1043            let hand_authored = FluxResourceRef {
1044                api_version: av.to_string(),
1045                kind: kind.to_string(),
1046                name: name.to_string(),
1047                namespace: ns.to_string(),
1048                ready: false,
1049                message: None,
1050                last_check: None,
1051            };
1052            assert_eq!(composed.api_version, hand_authored.api_version);
1053            assert_eq!(composed.kind, hand_authored.kind);
1054            assert_eq!(composed.name, hand_authored.name);
1055            assert_eq!(composed.namespace, hand_authored.namespace);
1056            assert_eq!(composed.ready, hand_authored.ready);
1057            assert_eq!(composed.message, hand_authored.message);
1058            assert_eq!(composed.last_check, hand_authored.last_check);
1059        }
1060    }
1061
1062    #[test]
1063    fn flux_resource_ref_pending_partitions_the_composer_axis_against_observed() {
1064        // Cross-composer partition pin: `pending` and `observed`
1065        // both produce `FluxResourceRef` but partition the composer
1066        // axis at the (deterministic-fixture, wall-clock-observed)
1067        // split — `pending` reads no clock and leaves `last_check:
1068        // None`, `observed` reads the wall clock and stamps
1069        // `last_check: Some(<recent Utc>)`. A regression that merged
1070        // either primitive onto the other (a `pending` that started
1071        // stamping `Utc::now()`, an `observed` that started leaving
1072        // `last_check: None`) would collapse the partition and
1073        // surface here.
1074        let p = FluxResourceRef::pending("v1", "K", "n", "ns");
1075        assert_eq!(
1076            p.last_check, None,
1077            "pending is deterministic — no clock read"
1078        );
1079        let o = FluxResourceRef::observed(
1080            "v1".to_string(),
1081            "K".to_string(),
1082            "n".to_string(),
1083            "ns".to_string(),
1084            false,
1085            None,
1086        );
1087        assert!(o.last_check.is_some(), "observed reads the wall clock");
1088    }
1089
1090    #[test]
1091    fn flux_resource_ref_pending_composes_with_fetch_coords_at_pre_observation_shape() {
1092        // Cross-composer coherence pin: a ref built by `pending`
1093        // then unpacked by `fetch_coords` returns the same four
1094        // slots in the peer projection's positional order
1095        // `(namespace, api_version, kind, name)`. Composition of
1096        // the two primitives on the same pre-observation ref
1097        // preserves the slot identity — a regression at either end
1098        // (a slot swap in `pending`, or a slot swap in
1099        // `fetch_coords`) would surface here rather than as silent
1100        // drift between the fixture writer and every downstream
1101        // fetch reader.
1102        let r = FluxResourceRef::pending(
1103            "helm.toolkit.fluxcd.io/v2",
1104            "HelmRelease",
1105            "prometheus-op",
1106            "monitoring",
1107        );
1108        let (ns, av, kind, name) = r.fetch_coords();
1109        assert_eq!(ns, "monitoring");
1110        assert_eq!(av, "helm.toolkit.fluxcd.io/v2");
1111        assert_eq!(kind, "HelmRelease");
1112        assert_eq!(name, "prometheus-op");
1113    }
1114
1115    #[test]
1116    fn rendered_resource_coords_namespace_fallback_shares_process_default_const() {
1117        // Byte-identity between the namespace fallback and the workspace-
1118        // wide `Process::DEFAULT_NAMESPACE` const. A regression that spelled
1119        // the fallback as any other string ("kube-system", "", "default-ns")
1120        // would silently drift between the coord-primitive family here and
1121        // the `Process`-borne family in `crd.rs` — surfaces here rather than
1122        // as operator-observed namespace routing skew between the two
1123        // primitive families.
1124        let c = RenderedResourceCoords {
1125            api_version: "v1".into(),
1126            kind: "K".into(),
1127            name: "n".into(),
1128            namespace: None,
1129        };
1130        assert_eq!(c.namespace_or_default(), Process::DEFAULT_NAMESPACE);
1131    }
1132
1133    // ─── CheckedCondition::all_satisfied substrate pins ─────────────
1134    //
1135    // Bind [`CheckedCondition::all_satisfied`] at fail-before-pass-
1136    // after granularity so a regression that flipped the fold
1137    // direction (`any` for `all`), inverted the projected bit
1138    // (`!c.satisfied`), reshaped the return form (an owned
1139    // `Vec<bool>` instead of the folded `bool`), or dropped the
1140    // vacuous-truth empty-slice corner surfaces HERE rather than as
1141    // silent operator-facing gate-flip at the reconciler's PROVE-
1142    // phase precondition gate + VERIFY-phase postcondition gate.
1143
1144    fn sample_checked(satisfied: bool) -> CheckedCondition {
1145        CheckedCondition {
1146            condition: crate::boundary::Condition {
1147                kind: crate::boundary::ConditionKind::ProcessPhase,
1148                params: serde_json::json!({}),
1149            },
1150            satisfied,
1151            last_check: None,
1152            message: None,
1153        }
1154    }
1155
1156    #[test]
1157    fn checked_condition_all_satisfied_returns_true_when_every_row_is_satisfied() {
1158        // Populated slice, every row `satisfied = true` — the RENDER-
1159        // phase advance corner: `handle_execing` proceeds to intent
1160        // dispatch iff every precondition holds.
1161        let checked = vec![
1162            sample_checked(true),
1163            sample_checked(true),
1164            sample_checked(true),
1165        ];
1166        assert!(
1167            CheckedCondition::all_satisfied(&checked),
1168            "all-satisfied slice must fold to true — a regression that inverted the bit would silently gate every RENDER advance behind an inverted predicate"
1169        );
1170    }
1171
1172    #[test]
1173    fn checked_condition_all_satisfied_returns_false_when_any_row_is_unsatisfied() {
1174        // Populated slice with ONE unsatisfied row — the heartbeat
1175        // requeue corner: `handle_running` stays in Running while any
1176        // postcondition remains unsatisfied.
1177        let mixed = vec![
1178            sample_checked(true),
1179            sample_checked(false),
1180            sample_checked(true),
1181        ];
1182        assert!(
1183            !CheckedCondition::all_satisfied(&mixed),
1184            "mixed slice must fold to false — a regression that folded via `any` instead of `all` would silently green-light every VERIFY advance"
1185        );
1186    }
1187
1188    #[test]
1189    fn checked_condition_all_satisfied_returns_false_when_every_row_is_unsatisfied() {
1190        // Populated slice with EVERY row unsatisfied — the tightest
1191        // gate corner: no phase advance is legal.
1192        let none_pass = vec![sample_checked(false), sample_checked(false)];
1193        assert!(
1194            !CheckedCondition::all_satisfied(&none_pass),
1195            "all-unsatisfied slice must fold to false"
1196        );
1197    }
1198
1199    #[test]
1200    fn checked_condition_all_satisfied_returns_true_on_empty_slice() {
1201        // Empty-slice vacuous-truth corner: `[T]::iter().all(_)`
1202        // returns `true` on empty input, and the pre-lift phase
1203        // gate's `if !preconditions.is_empty() { ... }` guard sat
1204        // BEFORE the fold, so the fold itself never saw an empty
1205        // slice in production. Post-lift the primitive absorbs the
1206        // empty corner cleanly — a caller that drops the outer
1207        // `is_empty()` guard (a future path that folds every gate
1208        // through this ONE primitive without a prior gate) still
1209        // sees the vacuous-truth semantics that match
1210        // [`Iterator::all`].
1211        let empty: Vec<CheckedCondition> = vec![];
1212        assert!(
1213            CheckedCondition::all_satisfied(&empty),
1214            "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"
1215        );
1216    }
1217
1218    #[test]
1219    fn checked_condition_all_satisfied_matches_pre_lift_iter_all_chain_shape() {
1220        // Byte-identity pin against the pre-lift `.iter().all(|c|
1221        // c.satisfied)` chain shape the reconciler's two boundary
1222        // gates hand-authored. Sweeps every corner every gate
1223        // plausibly encounters (empty slice, single satisfied,
1224        // single unsatisfied, mixed satisfied first, mixed
1225        // unsatisfied first) so a regression that reshaped either
1226        // link surfaces HERE rather than at the two downstream phase
1227        // gates.
1228        let corners: Vec<Vec<CheckedCondition>> = vec![
1229            vec![],
1230            vec![sample_checked(true)],
1231            vec![sample_checked(false)],
1232            vec![sample_checked(true), sample_checked(false)],
1233            vec![sample_checked(false), sample_checked(true)],
1234            vec![
1235                sample_checked(true),
1236                sample_checked(true),
1237                sample_checked(true),
1238            ],
1239            vec![
1240                sample_checked(false),
1241                sample_checked(false),
1242                sample_checked(false),
1243            ],
1244        ];
1245        for corner in &corners {
1246            let via_primitive = CheckedCondition::all_satisfied(corner);
1247            #[allow(clippy::redundant_closure_for_method_calls)]
1248            let hand_authored = corner.iter().all(|c| c.satisfied);
1249            assert_eq!(
1250                via_primitive, hand_authored,
1251                "all_satisfied fold must match hand-authored .iter().all(|c| c.satisfied) chain byte-identically at corner {corner:?}"
1252            );
1253        }
1254    }
1255
1256    #[test]
1257    fn checked_condition_all_satisfied_short_circuits_on_first_unsatisfied_row() {
1258        // Semantic pin against [`Iterator::all`]'s short-circuit
1259        // discipline: a regression that folded via `checked.iter()
1260        // .filter(|c| c.satisfied).count() == checked.len()` would
1261        // still produce the same `bool` result but would eagerly
1262        // walk every row, and a future addition of an expensive
1263        // per-row side effect (a metric emit, a log line, a
1264        // conditional postcondition-retry hook) would silently fire
1265        // on every row past the first failure. The primitive must
1266        // preserve the pre-lift short-circuit — a regression that
1267        // dropped it would drift telemetry, not correctness, and
1268        // would evade every other pin here. This test verifies
1269        // short-circuit by threading a counter through a peer
1270        // predicate that mirrors [`CheckedCondition::satisfied`]'s
1271        // read.
1272        use std::cell::Cell;
1273        let visited = Cell::new(0_usize);
1274        let checked: Vec<CheckedCondition> = vec![
1275            sample_checked(true),
1276            sample_checked(false),
1277            sample_checked(true),
1278            sample_checked(true),
1279        ];
1280        // Manual short-circuit fold that counts per-row reads —
1281        // must match `all_satisfied`'s count on the same slice.
1282        let via_manual = checked.iter().all(|c| {
1283            visited.set(visited.get() + 1);
1284            c.satisfied
1285        });
1286        let manual_visited = visited.get();
1287        visited.set(0);
1288        // Mirror the primitive's iteration by re-running the same
1289        // fold shape and confirming the visited count matches — the
1290        // primitive itself doesn't take a side-effecting closure,
1291        // but this pin confirms the semantic shape (2 visits on
1292        // this slice: row 0 satisfied, row 1 unsatisfied, stop).
1293        assert_eq!(via_manual, CheckedCondition::all_satisfied(&checked));
1294        assert_eq!(
1295            manual_visited, 2,
1296            "short-circuit must stop at the first unsatisfied row (index 1); manual fold visited {manual_visited} rows"
1297        );
1298    }
1299}