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    pub fn ready(reason: impl Into<String>, message: Option<String>) -> Self {
30        Self {
31            type_: ProcessConditionType::Ready.as_wire_str().into(),
32            status: K8sConditionStatus::True.as_wire_str().into(),
33            last_transition_time: Utc::now(),
34            reason: Some(reason.into()),
35            message,
36        }
37    }
38
39    pub fn not_ready(reason: impl Into<String>, message: impl Into<String>) -> Self {
40        Self {
41            type_: ProcessConditionType::Ready.as_wire_str().into(),
42            status: K8sConditionStatus::False.as_wire_str().into(),
43            last_transition_time: Utc::now(),
44            reason: Some(reason.into()),
45            message: Some(message.into()),
46        }
47    }
48
49    pub fn attested(root: &str) -> Self {
50        Self {
51            type_: ProcessConditionType::Attested.as_wire_str().into(),
52            status: K8sConditionStatus::True.as_wire_str().into(),
53            last_transition_time: Utc::now(),
54            reason: Some("AttestationWritten".into()),
55            message: Some(format!("composed_root={root}")),
56        }
57    }
58}
59
60/// Reference to a FluxCD resource emitted as part of this Process.
61#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
62#[serde(rename_all = "camelCase")]
63pub struct FluxResourceRef {
64    pub api_version: String,
65    pub kind: String,
66    pub name: String,
67    pub namespace: String,
68    #[serde(default)]
69    pub ready: bool,
70    #[serde(default, skip_serializing_if = "Option::is_none")]
71    pub message: Option<String>,
72    #[serde(default, skip_serializing_if = "Option::is_none")]
73    pub last_check: Option<DateTime<Utc>>,
74}
75
76impl FluxResourceRef {
77    /// Pure typed projection of the four fetch coordinates
78    /// `(namespace, api_version, kind, name)` every consumer that
79    /// dispatches this persisted reference through kube-rs's dynamic-
80    /// object surface splats by hand pre-lift. The 4-tuple binds the
81    /// slot order at ONE typed accessor so a copy-paste at any downstream
82    /// consumer cannot swap two adjacent `&str` slots in the fetch call.
83    ///
84    /// Peer projection to
85    /// [`crate::k8s_wire_identity::K8sWireIdentity`] on the static-
86    /// identity axis: [`K8sWireIdentity`] carries a
87    /// `(&'static str, &'static str)` closed-set variant's pair for
88    /// emit-time (RENDER phase) composition; this method carries the
89    /// full `(ns, apiVersion, kind, name)` 4-slot borrow for fetch-time
90    /// (VERIFY / ATTEST-heartbeat) composition where the ref's payload
91    /// comes back off the persisted `ProcessStatus.flux_resources`
92    /// slice with owned `String`s rather than static literals. The two
93    /// primitives partition the fetch axis by whether the caller starts
94    /// from a closed-set variant (emit-time) or a persisted status
95    /// slice (fetch-time).
96    ///
97    /// Pre-lift the 5-slot `ssapply::fetch(client, &r.namespace,
98    /// &r.api_version, &r.kind, &r.name)` splat was hand-authored at
99    /// TWO sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold
100    /// in `tatara-reconciler::phase_machine`:
101    /// * `handle_running` — the VERIFY-phase per-ref readiness probe
102    ///   that populates the updated `FluxResourceRef` slice with
103    ///   `ready` + `message` + `last_check`.
104    /// * `handle_attested` — the ATTEST-heartbeat drift detector that
105    ///   short-circuits on the first non-Ready ref.
106    ///
107    /// Both sites splatted the SAME four `&r.X` field borrows in the
108    /// SAME order into raw `ssapply::fetch`. A copy-paste that swapped
109    /// two adjacent `&str` slots (`&r.api_version` and `&r.kind` are
110    /// both strings that look interchangeable to a mechanical
111    /// substitution) would silently 404 at wire time and diagnose as a
112    /// broken CRD rather than as slot skew at the callsite. Post-lift
113    /// each site names the ref ONCE and unpacks it through this ONE
114    /// projection; the slot order binds structurally at the tuple
115    /// return so a caller cannot desync one axis.
116    ///
117    /// A future addition (a case-fold normalization on the group, a
118    /// virtual-cluster prefix rewrite for multi-tenancy, a
119    /// `generateName` fallback on the name slot, a cluster-cache
120    /// short-circuit inserted between the projection and the fetch
121    /// call) lands at this ONE method and every downstream fetch
122    /// consumer inherits the upgrade mechanically — no per-site edit
123    /// at `handle_running` / `handle_attested` / any future kenshi-
124    /// runner / mirror-audit / drift-probe consumer that grows a third
125    /// consumer.
126    ///
127    /// Return-order pin lives at
128    /// [`tests::flux_resource_ref_fetch_coords_binds_slots_by_position`]
129    /// so a regression that swapped `namespace` and `api_version`
130    /// (both `String`, same type) inside the tuple constructor fails-
131    /// loudly here rather than as a silent wire-time 404 at every
132    /// downstream fetch consumer.
133    ///
134    /// Theory grounding: THEORY.md §II.1 invariant 5 (composition
135    /// preserves proofs — the 4-tuple slot order binds at ONE typed
136    /// projection so a regression across the two fields of the same
137    /// `String` type fails at the projection's positional pin rather
138    /// than at every downstream fetch consumer). THEORY.md §VI.1
139    /// (generation over composition — the 5-slot splat recurred at
140    /// two hand-authored sites past the ≥ 2 duplication trigger, and
141    /// is lifted to ONE typed borrow-projection here).
142    pub fn fetch_coords(&self) -> (&str, &str, &str, &str) {
143        (&self.namespace, &self.api_version, &self.kind, &self.name)
144    }
145
146    /// Compose a `FluxResourceRef` stamped at "observed now" — the
147    /// `last_check` slot is set to `Some(Utc::now())` at ONE substrate
148    /// owner, and the four coordinate slots + `ready` + `message`
149    /// are bound positionally so a slot-swap regression surfaces at
150    /// the constructor's positional pin rather than as silent drift
151    /// at every downstream `ProcessStatus.flux_resources` writer.
152    ///
153    /// Pre-lift the 7-slot `FluxResourceRef { …, last_check:
154    /// Some(chrono::Utc::now()) }` struct-literal was hand-authored
155    /// at TWO sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
156    /// threshold in `tatara-reconciler::phase_machine`:
157    /// * `handle_running` — the VERIFY-phase per-ref rebuild that
158    ///   restamps each polled ref with fresh `ready` + `message` +
159    ///   `last_check`.
160    /// * `flux_ref_from_json` — the post-SSA initial-state seeder
161    ///   that stamps a freshly-applied ref as `ready = false`,
162    ///   `message = Some("applied; awaiting reconciliation")`,
163    ///   `last_check = Some(Utc::now())`.
164    ///
165    /// Both sites restated the SAME seven field bindings in the
166    /// SAME order, and both restated the SAME `Some(chrono::Utc::
167    /// now())` stamp. A copy-paste that swapped two adjacent
168    /// `String` slots (`api_version` and `kind`, `kind` and `name`,
169    /// or `name` and `namespace` are all mechanically
170    /// indistinguishable at the type level) would silently persist
171    /// a slot-inverted ref that the downstream Flux fetch consumer
172    /// (via [`Self::fetch_coords`]) would then 404 on. Post-lift
173    /// both sites name the six inputs ONCE and route through this
174    /// ONE composer; the seventh slot (`last_check`) is stamped at
175    /// the composer's body so a future injection point (a fake
176    /// clock for testing, a monotonic-clock cross-check, a per-
177    /// fleet skew tolerance) lands at ONE substrate site rather
178    /// than at every hand-authored `Some(chrono::Utc::now())` stamp.
179    ///
180    /// Return-order pin lives at
181    /// [`tests::flux_resource_ref_observed_binds_slots_by_position`]
182    /// so a regression that swapped `api_version` and `kind` (both
183    /// `String`, same type) inside the constructor's argument list
184    /// fails-loudly here rather than as a silent wire-time 404 at
185    /// every downstream fetch consumer.
186    ///
187    /// Theory grounding: THEORY.md §II.1 invariant 5 (composition
188    /// preserves proofs — the 6-slot positional binding + the
189    /// `last_check` stamp compose at ONE typed owner, so a
190    /// regression across the four `String` coordinate slots fails
191    /// at the composer's positional pin rather than at every
192    /// downstream Flux status writer). THEORY.md §VI.1 (generation
193    /// over composition — the 7-slot struct-literal recurred at two
194    /// hand-authored sites past the ≥ 2 duplication trigger, and is
195    /// lifted to ONE typed composer here).
196    pub fn observed(
197        api_version: String,
198        kind: String,
199        name: String,
200        namespace: String,
201        ready: bool,
202        message: Option<String>,
203    ) -> Self {
204        Self {
205            api_version,
206            kind,
207            name,
208            namespace,
209            ready,
210            message,
211            last_check: Some(Utc::now()),
212        }
213    }
214
215    /// Compose a `FluxResourceRef` in the pre-observation shape — the
216    /// 4-slot coordinate binding with the three status slots defaulted
217    /// (`ready: false`, `message: None`, `last_check: None`). The
218    /// deterministic-fixture peer of [`Self::observed`] on the same
219    /// `→ FluxResourceRef` composer axis: `observed` reads the wall
220    /// clock and takes 6 args (a live post-fetch stamp), `pending`
221    /// reads no clock and takes 4 args (a pre-observation fixture
222    /// seed, and the natural base for `..base.clone()` spread updates
223    /// that vary a single slot for a per-corner test sweep).
224    ///
225    /// Pre-lift the SAME 7-slot `FluxResourceRef { api_version, kind,
226    /// name, namespace, ready: false, message: None, last_check: None
227    /// }` struct-literal was hand-authored at THREE workspace-wide
228    /// fixture sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
229    /// threshold:
230    ///
231    /// * [`crate::crd`]
232    ///   `crd::observed_flux_resources_tests::sample_flux_ref(name)`
233    ///   — the shared `Kustomization`/`flux-system` fixture the
234    ///   `Process::observed_flux_resources` pin family destructures
235    ///   for its `flux_resources`-populated corners.
236    /// * `tatara-reconciler::ssapply::tests::sample_flux_ref_for_diag`
237    ///   — the `HelmRelease`/`flux-system` fixture the
238    ///   `flux_ref_fetch_error_context` diagnostic-wording pin
239    ///   family destructures for its (kind, name) slot-coverage
240    ///   sweep.
241    /// * `tatara-reconciler::ssapply::tests::
242    ///   flux_ref_fetch_error_context_matches_pre_lift_hand_authored_wording`
243    ///   — the inline 7-slot literal inside the cross-substrate
244    ///   coherence pin's per-case sweep over three distinct
245    ///   `(api_version, kind, name, namespace)` tuples.
246    ///
247    /// All THREE sites restated the SAME seven field bindings in the
248    /// SAME order and the SAME three defaulted status slots (`ready:
249    /// false, message: None, last_check: None`), differing only in
250    /// the four coordinate `String` values. Post-lift each callsite
251    /// reads `FluxResourceRef::pending(<api_version>, <kind>, <name>,
252    /// <namespace>)` and the four-slot bind + three-slot default
253    /// sinks live at ONE substrate owner.
254    ///
255    /// The `impl Into<String>` signature accepts BOTH `&'static str`
256    /// (the fixture-helper sites that spell coordinate literals
257    /// inline) AND owned `String` (a future callsite handing off a
258    /// dynamically-derived coordinate) without widening. Matches the
259    /// discipline of the sibling substrate composers
260    /// [`crate::pool::PoolMember::unallocated`] +
261    /// [`crate::allocation::AllocationRef::new`] on the identity-slot
262    /// axis.
263    ///
264    /// A future normalization (a case-fold on the group, a
265    /// virtual-cluster prefix rewrite for multi-tenancy, a stricter
266    /// kind gate, a `generateName` fallback on the name slot, a
267    /// canonical rename of one of the three defaulted status slots
268    /// to a typed `PreObservation` marker) lands at THIS ONE
269    /// substrate primitive and every downstream fixture / helper
270    /// inherits the upgrade mechanically — no per-site edit at any
271    /// of the THREE listed callers or at future consumers (a
272    /// stable-name claim-arbiter's pending-ref seed, a kenshi-runner
273    /// pre-observation fixture, a mirror-audit drift-probe test
274    /// helper).
275    ///
276    /// Theory grounding: THEORY.md §II.1 invariant 5 (composition
277    /// preserves proofs — the 4-slot positional binding + the three
278    /// defaulted status slots compose at ONE typed owner, so a
279    /// regression across the four `String` coordinate slots fails at
280    /// the composer's positional pin rather than at every downstream
281    /// fixture consumer). THEORY.md §VI.1 (generation over
282    /// composition — the 7-slot struct-literal recurred at three
283    /// hand-authored fixture sites past the ≥ 2 duplication trigger,
284    /// and is lifted to ONE typed composer here).
285    #[must_use]
286    pub fn pending(
287        api_version: impl Into<String>,
288        kind: impl Into<String>,
289        name: impl Into<String>,
290        namespace: impl Into<String>,
291    ) -> Self {
292        Self {
293            api_version: api_version.into(),
294            kind: kind.into(),
295            name: name.into(),
296            namespace: namespace.into(),
297            ready: false,
298            message: None,
299            last_check: None,
300        }
301    }
302}
303
304/// Identifying coordinates of a rendered K8s resource — the
305/// `(apiVersion, kind, metadata.name, metadata.namespace)` 4-tuple
306/// every consumer that walks a rendered `serde_json::Value` resource
307/// unwraps by hand pre-lift.
308///
309/// The three K8s API-path segments (`apiVersion`, `kind`,
310/// `metadata.name`) are REQUIRED — a rendered resource missing any
311/// of them cannot be applied via kube-rs's dynamic API surface, so
312/// the extraction fails fast at the boundary rather than as a
313/// downstream `Api::patch` panic. `metadata.namespace` is
314/// intentionally kept as `Option<String>` because different consumers
315/// resolve the fallback differently: `apply_owned` uses the
316/// caller-supplied `namespace: &str` argument (the reconciler already
317/// resolved the target namespace upstream), while `flux_ref_from_json`
318/// records the K8s canonical `"default"` fallback into the persisted
319/// `FluxResourceRef.namespace` slot. The peer method
320/// [`Self::namespace_or_default`] applies the K8s canonical fallback
321/// (`Process::DEFAULT_NAMESPACE = "default"`) for consumers wanting
322/// the same shape [`FluxResourceRef.namespace`] carries.
323///
324/// Pre-lift the 3+1 slot extraction was hand-authored at TWO sites
325/// past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold across
326/// `tatara-reconciler`:
327/// * `tatara-reconciler::phase_machine::flux_ref_from_json` — the
328///   post-SSA `FluxResourceRef` builder that persists into
329///   `ProcessStatus.flux_resources`; namespace half fallback-
330///   defaulted to `"default"`.
331/// * `tatara-reconciler::ssapply::apply_owned` — the SSA entry
332///   point that extracts (apiVersion, kind, name) for the
333///   [`kube::Api::patch`] call; namespace half discarded (the
334///   `namespace: &str` argument comes from the caller upstream).
335///
336/// Both callsites restated the same three
337/// `.get(K).and_then(|v| v.as_str()).ok_or_else(|| anyhow!(...))?
338/// .to_string()` incantations with subtly different error wording
339/// (`"resource missing X"` vs `"rendered resource missing X"`); post-
340/// lift both route through this ONE substrate owner with the
341/// canonical `"rendered resource missing X"` wording. A future
342/// addition (case-fold on the group, a rename of the namespace
343/// fallback, a stricter kind gate, a Unicode-safe collation step,
344/// support for `metadata.generateName` as a name fallback) lands at
345/// the primitive's body on the substrate, not at 2 independent
346/// hand-writes across 2 reconciler files.
347///
348/// Namespace fallback const is shared with
349/// [`Process::DEFAULT_NAMESPACE`] — a rename of the K8s canonical
350/// default namespace lands at that ONE workspace-wide const, not at
351/// per-primitive local literals that would drift silently.
352#[derive(Clone, Debug, PartialEq, Eq)]
353pub struct RenderedResourceCoords {
354    /// `apiVersion` — the group+version pair kube-rs uses to resolve
355    /// the `ApiResource` for the SSA call.
356    pub api_version: String,
357    /// `kind` — the resource kind (Kustomization, HelmRelease, …).
358    pub kind: String,
359    /// `metadata.name` — the API-path leaf segment.
360    pub name: String,
361    /// `metadata.namespace` — raw from the resource, `None` when the
362    /// slot is absent (a cluster-scoped resource, or a namespaced
363    /// resource whose namespace was left for the API server to
364    /// substitute). Consumers apply their own fallback:
365    /// [`Self::namespace_or_default`] applies the K8s canonical
366    /// `"default"` (matching what [`FluxResourceRef.namespace`]
367    /// records); other consumers substitute a caller-supplied string
368    /// (see `tatara-reconciler::ssapply::apply_owned`).
369    pub namespace: Option<String>,
370}
371
372impl RenderedResourceCoords {
373    /// Extract the 4-tuple from a rendered K8s resource JSON `Value`.
374    ///
375    /// Fails with a canonical `"rendered resource missing X"` message
376    /// when any of the three required slots (`apiVersion`, `kind`,
377    /// `metadata.name`) is absent or non-string; `metadata.namespace`
378    /// is optional and captured as `None` when absent.
379    ///
380    /// The error wording is pinned by
381    /// [`tests::rendered_resource_coords_error_wording_is_canonical`]
382    /// so a regression that reshaped the message surfaces at the test
383    /// surface rather than as silent drift between the two pre-lift
384    /// call sites (which used subtly different wording — `"resource
385    /// missing X"` in `apply_owned` vs `"rendered resource missing
386    /// X"` in `flux_ref_from_json`).
387    pub fn from_json(res: &Value) -> anyhow::Result<Self> {
388        // The three REQUIRED-slot extracts (`apiVersion`, `kind`,
389        // `metadata.name`) route through the ONE substrate primitive
390        // `Self::required_str` — the required-extract sibling of
391        // `crate::json_object::ValueGetExt::get_str` on the same
392        // rendered-resource axis. A future normalization (Unicode
393        // NFC-fold, whitespace trim, empty-string rejection) lands
394        // at the primitive body and every downstream consumer of the
395        // canonical `"rendered resource missing X"` wire form inherits
396        // it mechanically. The optional `metadata.namespace` slot
397        // routes directly through the ONE substrate primitive
398        // `crate::json_object::ValueGetExt::get_str` on the
399        // `Option<&Value>` receiver arm — the outer-optionality
400        // widening added alongside the `Value` + `Map<String, Value>`
401        // impls. Pre-lift this callsite hand-authored the outer-
402        // optionality closure `metadata.and_then(|m| m.get_str
403        // ("namespace"))` past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
404        // threshold (this site + `Self::required_str`'s body); post-
405        // lift the closure disappears and the axis-family method-call
406        // surface stays identical to the `Value` / `Map` receiver
407        // callers.
408        let api_version = Self::required_str(Some(res), "apiVersion", "apiVersion")?;
409        let kind = Self::required_str(Some(res), "kind", "kind")?;
410        let metadata = res.get("metadata");
411        let name = Self::required_str(metadata, "name", "metadata.name")?;
412        let namespace = metadata.get_str("namespace").map(str::to_string);
413        Ok(Self {
414            api_version,
415            kind,
416            name,
417            namespace,
418        })
419    }
420
421    /// Diagnostic prefix stamped ahead of every required-slot label in
422    /// the canonical error wire form. Owned in ONE workspace-wide place
423    /// so a rename (a fleet-wide switch to `"resource is missing"` /
424    /// `"missing rendered-resource field"`) lands here and every
425    /// downstream `.to_string()`-consumer + operator-facing log grep
426    /// inherits the rename mechanically, not at 3 hand-authored
427    /// `anyhow!(…)` restatements.
428    pub const MISSING_MESSAGE_PREFIX: &'static str = "rendered resource missing";
429
430    /// Required-slot extract on a rendered-resource JSON `Value` — the
431    /// substrate owner of the paired `.get_str(<key>).ok_or_else(||
432    /// anyhow!("rendered resource missing <slot>"))?.to_string()`
433    /// four-link chain every REQUIRED slot on a rendered `Value`
434    /// walks pre-lift.
435    ///
436    /// The primitive accepts an `Option<&Value>` receiver so BOTH
437    /// shallow reads (top-level `apiVersion` / `kind` on the resource
438    /// root, callers thread `Some(res)`) AND one-level-nested reads
439    /// (`metadata.name` walking through `res.get("metadata")`,
440    /// callers thread the `Option<&Value>` handle the `.get()` step
441    /// returns) reach the same owner. The `key` slot is the wire-form
442    /// name the underlying [`ValueGetExt::get_str`] looks up on the
443    /// object; the `error_slot` slot is the diagnostic label stamped
444    /// into the error's `Display` output. The two are decoupled so
445    /// `metadata.name` can look up `"name"` on the `metadata` sub-
446    /// object while reporting the dotted `"metadata.name"` path an
447    /// operator bisecting a fault sees in the log.
448    ///
449    /// Ok arm returns `String` (owned) rather than the borrowed
450    /// `&str` [`ValueGetExt::get_str`] returns — every downstream
451    /// slot on the [`RenderedResourceCoords`] struct is an owned
452    /// `String`, so the primitive absorbs the `str::to_string`
453    /// coerce that pre-lift lived at three hand-authored callsites.
454    /// Err arm carries an `anyhow::Error` whose `Display` reads
455    /// exactly `"<Self::MISSING_MESSAGE_PREFIX> <error_slot>"` —
456    /// byte-identical to the pre-lift hand-authored `anyhow!(
457    /// "rendered resource missing {slot}")` wire form.
458    ///
459    /// ### Fires on all four absent-shape corners
460    ///
461    /// The primitive returns `Err` on ALL four ways a required
462    /// slot can miss:
463    ///
464    /// 1. Receiver is `None` — the `metadata.name` corner when the
465    ///    top-level `metadata` object itself is absent (the caller
466    ///    threaded `res.get("metadata")` which returned `None`).
467    /// 2. Slot is absent — the receiver is present but does not
468    ///    carry a value at `key`.
469    /// 3. Slot is present but non-string — a fixture bug that
470    ///    stamped a JSON number / bool / object / array at the
471    ///    slot; the `get_str` step falls through and the primitive
472    ///    reports the slot as missing (matching the pre-lift
473    ///    behavior where every non-string variant surfaced as the
474    ///    same `"missing"` diagnostic — pinning "cannot be applied
475    ///    via kube-rs's dynamic API surface" as the shared
476    ///    failure mode).
477    /// 4. Receiver is non-object — a resource authored as a JSON
478    ///    array / string / null at any of the levels the primitive
479    ///    walks (the `get_str` step returns `None` verbatim).
480    ///
481    /// All four corners produce the SAME wire form so an operator's
482    /// `rg "rendered resource missing"` sweep hits exactly one
483    /// footprint per faulted slot, not four differently-worded
484    /// diagnostics per absent-shape variant.
485    ///
486    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
487    /// the 4-link `.get_str(<key>).ok_or_else(|| anyhow!("rendered
488    /// resource missing <slot>"))?.to_string()` shape recurred at 3
489    /// hand-authored sites past the ★★ PRIME-DIRECTIVE ≥ 2
490    /// duplication trigger, and is lifted to ONE substrate owner
491    /// here). THEORY.md §II.1 invariant 5 (composition preserves
492    /// proofs — a regression that drifted the diagnostic prefix
493    /// wording at ONE site would silently pass the two sibling
494    /// pins and fail HERE; post-lift the wire form is owned once
495    /// at [`Self::MISSING_MESSAGE_PREFIX`] and every downstream
496    /// composition inherits the rename mechanically).
497    fn required_str(
498        v: Option<&Value>,
499        key: &'static str,
500        error_slot: &'static str,
501    ) -> anyhow::Result<String> {
502        // The `Option<&Value>` outer-optionality unwrap-then-project
503        // step rides through the substrate primitive
504        // `crate::json_object::ValueGetExt::get_str` on the
505        // `Option<&Value>` receiver arm — the outer-optionality
506        // widening sibling of the `Value` / `Map<String, Value>`
507        // impls. Pre-lift this body hand-authored the outer-
508        // optionality closure `v.and_then(|x| x.get_str(key))` past
509        // the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold (this
510        // helper's body + the sibling `metadata.namespace` walk in
511        // `Self::from_json`); post-lift the closure disappears and
512        // the axis-family method-call surface stays identical to the
513        // `Value` / `Map` receiver callers.
514        v.get_str(key).map(str::to_string).ok_or_else(|| {
515            anyhow::anyhow!(
516                "{prefix} {slot}",
517                prefix = Self::MISSING_MESSAGE_PREFIX,
518                slot = error_slot,
519            )
520        })
521    }
522
523    /// `metadata.namespace` slice with the K8s canonical `"default"`
524    /// fallback applied — matching what [`Process::DEFAULT_NAMESPACE`]
525    /// spells for the `Process`-borne coordinate primitive family
526    /// and what [`FluxResourceRef.namespace`] records into
527    /// `ProcessStatus.flux_resources`.
528    pub fn namespace_or_default(&self) -> &str {
529        self.namespace
530            .as_deref()
531            .unwrap_or(Process::DEFAULT_NAMESPACE)
532    }
533}
534
535/// A boundary condition paired with its current satisfaction state.
536#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
537#[serde(rename_all = "camelCase")]
538pub struct CheckedCondition {
539    #[serde(flatten)]
540    pub condition: Condition,
541    pub satisfied: bool,
542    #[serde(default, skip_serializing_if = "Option::is_none")]
543    pub last_check: Option<DateTime<Utc>>,
544    #[serde(default, skip_serializing_if = "Option::is_none")]
545    pub message: Option<String>,
546}
547
548impl CheckedCondition {
549    /// True iff every [`CheckedCondition`] in the slice has
550    /// `satisfied == true` — the ONE-line collapse of the paired
551    /// `checked.iter().all(|c| c.satisfied)` incantation the
552    /// reconciler's precondition + postcondition boundary gates both
553    /// spelled by hand pre-lift.
554    ///
555    /// Pre-lift the SAME `.iter().all(|c| c.satisfied)` chain was
556    /// hand-authored at TWO sites past the ★★ PRIME-DIRECTIVE ≥ 2
557    /// duplication threshold in `tatara-reconciler::phase_machine`,
558    /// each walking the SAME `Vec<CheckedCondition>` → `bool`
559    /// projection to gate a phase transition on a boundary predicate:
560    /// * `handle_execing` — the PROVE-phase precondition gate that
561    ///   stays in Execing (heartbeat requeue) while any precondition
562    ///   remains unsatisfied and proceeds to RENDER only when every
563    ///   precondition holds.
564    /// * `handle_running` — the VERIFY-phase postcondition gate that
565    ///   stays in Running (heartbeat requeue) while any postcondition
566    ///   remains unsatisfied and advances to Attested only when every
567    ///   postcondition holds.
568    ///
569    /// Both sites walked the SAME `Iterator::all` short-circuit on the
570    /// SAME `bool` slot of the SAME struct. Post-lift both consumers
571    /// name the slice ONCE and route through this ONE primitive; the
572    /// vacuous-truth corner (empty slice → `true`, matching
573    /// [`Iterator::all`]'s empty-input identity) sits at ONE substrate
574    /// site so a future normalization (a per-slot weight overlay, a
575    /// per-kind override that treats `Warn`-severity failures as
576    /// satisfied, a compliance-baseline gate that requires N-of-M
577    /// rather than all-of-M) lands at ONE substrate function and both
578    /// downstream phase gates inherit the upgrade mechanically.
579    ///
580    /// Return-form axis: `bool` — the exact type each phase gate
581    /// pre-lift bound at `let all_pass = <chain>;` and immediately
582    /// consumed in a `!all_pass` short-circuit + a `message` slot's
583    /// ternary branch. The `&[Self]` argument accepts every pre-lift
584    /// slice provenance verbatim: a `&Vec<CheckedCondition>` (both
585    /// pre-lift sites had the `Vec` on the stack from
586    /// [`crate::phase_machine::evaluate_conditions`]'s owned return)
587    /// coerces through auto-deref, so no callsite has to change its
588    /// upstream provenance to route through the primitive.
589    ///
590    /// Peer to the sibling projection [`Self::satisfied`] on the (row
591    /// scope × predicate) axis pair: `satisfied` is the per-row
592    /// projection; `all_satisfied` is the slice-wide fold of the same
593    /// bit. Both live on `CheckedCondition` so a future rename or
594    /// per-slot normalization travels through the same owner without
595    /// splitting between "per-row" and "slice-wide" call sinks.
596    ///
597    /// Return-shape pin lives at
598    /// [`tests::checked_condition_all_satisfied_matches_pre_lift_iter_all_chain_shape`]
599    /// so a regression that flipped the fold direction (`any` for
600    /// `all`), inverted the bit (`!c.satisfied`), or reshaped the
601    /// return form (an owned `Vec<bool>` instead of the folded `bool`)
602    /// fails-loudly here rather than as silent operator-facing skew
603    /// between the pre-lift `if !all_pass { requeue }` gate and the
604    /// post-lift call — every downstream consumer would still
605    /// short-circuit but on inverted semantics.
606    ///
607    /// Theory grounding: THEORY.md §VI.1 (generation over composition
608    /// — the 1-line `.iter().all(...)` chain recurred at two hand-
609    /// authored sites past the ≥ 2 duplication trigger, and is lifted
610    /// to ONE typed fold here). THEORY.md §II.1 invariant 5
611    /// (composition preserves proofs — the empty-slice vacuous-truth
612    /// corner + the fold direction + the projected bit's polarity all
613    /// bind at ONE substrate site, so a regression across any of the
614    /// three surfaces at [`tests::checked_condition_all_satisfied_*`]
615    /// pin rather than as silent gate-flip at every downstream phase
616    /// handler).
617    #[must_use]
618    pub fn all_satisfied(checked: &[Self]) -> bool {
619        checked.iter().all(|c| c.satisfied)
620    }
621}
622
623/// Summary of boundary verification.
624#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
625#[serde(rename_all = "camelCase")]
626pub struct BoundaryStatus {
627    #[serde(default)]
628    pub preconditions: Vec<CheckedCondition>,
629    #[serde(default)]
630    pub postconditions: Vec<CheckedCondition>,
631    /// Absolute deadline for VERIFY (derived from `spec.boundary.timeout`).
632    #[serde(default, skip_serializing_if = "Option::is_none")]
633    pub deadline: Option<DateTime<Utc>>,
634}
635
636/// Summary of compliance checks at the latest attestation.
637#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
638#[serde(rename_all = "camelCase")]
639pub struct ComplianceStatus {
640    #[serde(default, skip_serializing_if = "Option::is_none")]
641    pub baseline: Option<String>,
642    pub satisfied: u32,
643    pub violated: u32,
644    pub total: u32,
645    #[serde(default)]
646    pub violations: Vec<String>,
647}
648
649#[cfg(test)]
650mod tests {
651    use super::*;
652    use serde_json::json;
653
654    // ─── ProcessCondition writer / K8sConditionStatus substrate pins ─
655    //
656    // Byte-shape parity pins between `ProcessCondition::{ready,
657    // not_ready, attested}` writer output and the pre-lift hand-
658    // authored `"True"` / `"False"` `status` slot literals every
659    // downstream K8s API server + K8s-Condition-reading peer depends
660    // on. Post-lift the writers compose through
661    // `K8sConditionStatus::<V>.as_wire_str()`; these pins catch a
662    // regression at the substrate primitive (a lower-case drift, a
663    // whitespace prefix, a `serde(rename)` addition on the enum)
664    // that would silently reshape every emitted Process
665    // `status.conditions[]` slot away from the K8s wire form.
666
667    /// Fail-before-pass-after: `ProcessCondition::ready` emits the
668    /// exact-case ASCII `"True"` on the `status` slot the K8s API
669    /// server accepts, byte-identical to the pre-lift hand-authored
670    /// `status: "True".into()` literal. A regression at the substrate
671    /// primitive (a `to_lowercase` pass, a case-drifted variant
672    /// literal in `k8s_condition::K8sConditionStatus::as_wire_str`)
673    /// surfaces HERE, not as silent operator-facing wire-form skew
674    /// on the emitted Process CRD.
675    #[test]
676    fn ready_writer_status_slot_matches_pre_lift_true_literal_bytewise() {
677        let c = ProcessCondition::ready("ObservedRunning", Some("healthy".into()));
678        assert_eq!(c.type_, "Ready");
679        assert_eq!(c.status, "True");
680    }
681
682    /// Fail-before-pass-after: `ProcessCondition::not_ready` emits
683    /// the exact-case ASCII `"False"` on the `status` slot, byte-
684    /// identical to the pre-lift hand-authored `status: "False".
685    /// into()` literal.
686    #[test]
687    fn not_ready_writer_status_slot_matches_pre_lift_false_literal_bytewise() {
688        let c = ProcessCondition::not_ready("ObservedFailed", "boom");
689        assert_eq!(c.type_, "Ready");
690        assert_eq!(c.status, "False");
691    }
692
693    /// Fail-before-pass-after: `ProcessCondition::attested` emits
694    /// the exact-case ASCII `"True"` on the `status` slot with the
695    /// `"Attested"` type row, byte-identical to the pre-lift hand-
696    /// authored `type_: "Attested".into()` + `status: "True".into()`
697    /// pair.
698    #[test]
699    fn attested_writer_status_slot_matches_pre_lift_true_literal_bytewise() {
700        let c = ProcessCondition::attested("blake3:abc123");
701        assert_eq!(c.type_, "Attested");
702        assert_eq!(c.status, "True");
703        // Message body preserves the composed_root diagnostic
704        // wording verbatim — the substrate lift only reshaped the
705        // `status` slot, not the human-facing message.
706        assert_eq!(c.message.as_deref(), Some("composed_root=blake3:abc123"));
707        assert_eq!(c.reason.as_deref(), Some("AttestationWritten"));
708    }
709
710    /// Writer/reader wire-form parity: the `status` slot every
711    /// writer here emits is the SAME byte-shape the
712    /// `K8sConditionStatus::from_wire_str` reader in
713    /// `tatara-reconciler::ssapply::ready_condition_value` accepts.
714    /// A regression that drifted `as_wire_str` at ONE variant would
715    /// silently desynchronize every writer/reader pair in the
716    /// workspace; this pin surfaces the drift at the substrate.
717    #[test]
718    fn writer_output_round_trips_through_from_wire_str() {
719        let ready = ProcessCondition::ready("R", None);
720        assert_eq!(
721            K8sConditionStatus::from_wire_str(&ready.status),
722            Some(K8sConditionStatus::True),
723        );
724        let not_ready = ProcessCondition::not_ready("R", "why");
725        assert_eq!(
726            K8sConditionStatus::from_wire_str(&not_ready.status),
727            Some(K8sConditionStatus::False),
728        );
729        let attested = ProcessCondition::attested("blake3:zzz");
730        assert_eq!(
731            K8sConditionStatus::from_wire_str(&attested.status),
732            Some(K8sConditionStatus::True),
733        );
734    }
735
736    // ─── RenderedResourceCoords substrate pins ──────────────────────
737
738    #[test]
739    fn rendered_resource_coords_from_json_extracts_all_four_slots_when_present() {
740        let res = json!({
741            "apiVersion": "kustomize.toolkit.fluxcd.io/v1",
742            "kind": "Kustomization",
743            "metadata": {
744                "name": "observability-stack",
745                "namespace": "flux-system",
746            },
747        });
748        let c = RenderedResourceCoords::from_json(&res).expect("extract");
749        assert_eq!(c.api_version, "kustomize.toolkit.fluxcd.io/v1");
750        assert_eq!(c.kind, "Kustomization");
751        assert_eq!(c.name, "observability-stack");
752        assert_eq!(c.namespace.as_deref(), Some("flux-system"));
753    }
754
755    #[test]
756    fn rendered_resource_coords_from_json_captures_absent_namespace_as_none() {
757        // Cluster-scoped resource — `metadata.namespace` intentionally absent.
758        let res = json!({
759            "apiVersion": "v1",
760            "kind": "Namespace",
761            "metadata": {"name": "demo-test"},
762        });
763        let c = RenderedResourceCoords::from_json(&res).expect("extract");
764        assert_eq!(c.namespace, None);
765        assert_eq!(c.name, "demo-test");
766    }
767
768    #[test]
769    fn rendered_resource_coords_from_json_errors_on_missing_api_version() {
770        let res = json!({"kind": "K", "metadata": {"name": "n"}});
771        let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
772        assert_eq!(e.to_string(), "rendered resource missing apiVersion");
773    }
774
775    #[test]
776    fn rendered_resource_coords_from_json_errors_on_missing_kind() {
777        let res = json!({"apiVersion": "v1", "metadata": {"name": "n"}});
778        let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
779        assert_eq!(e.to_string(), "rendered resource missing kind");
780    }
781
782    #[test]
783    fn rendered_resource_coords_from_json_errors_on_missing_metadata_name() {
784        let res = json!({"apiVersion": "v1", "kind": "K", "metadata": {}});
785        let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
786        assert_eq!(e.to_string(), "rendered resource missing metadata.name");
787    }
788
789    #[test]
790    fn rendered_resource_coords_from_json_errors_on_missing_metadata_object() {
791        // `metadata` absent entirely — same failure as `metadata.name` missing,
792        // because the API-path leaf segment cannot be resolved.
793        let res = json!({"apiVersion": "v1", "kind": "K"});
794        let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
795        assert_eq!(e.to_string(), "rendered resource missing metadata.name");
796    }
797
798    #[test]
799    fn rendered_resource_coords_from_json_errors_on_non_string_slot() {
800        // A numeric `apiVersion` slot falls through the `.as_str()` gate and
801        // triggers the same missing-slot failure as absence — the API-path
802        // segment is not a string.
803        let res = json!({
804            "apiVersion": 42,
805            "kind": "K",
806            "metadata": {"name": "n"},
807        });
808        let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
809        assert_eq!(e.to_string(), "rendered resource missing apiVersion");
810    }
811
812    #[test]
813    fn rendered_resource_coords_error_wording_is_canonical() {
814        // Pins the exact spelling every downstream consumer sees.
815        // Pre-lift wording differed across the two call sites (`"resource
816        // missing X"` in `apply_owned` vs `"rendered resource missing X"` in
817        // `flux_ref_from_json`); post-lift the canonical wording is
818        // `"rendered resource missing X"` at every site.
819        let cases = [
820            (
821                "apiVersion",
822                json!({"kind": "K", "metadata": {"name": "n"}}),
823            ),
824            (
825                "kind",
826                json!({"apiVersion": "v1", "metadata": {"name": "n"}}),
827            ),
828            (
829                "metadata.name",
830                json!({"apiVersion": "v1", "kind": "K", "metadata": {}}),
831            ),
832        ];
833        for (slot, res) in cases {
834            let e = RenderedResourceCoords::from_json(&res).expect_err("must error");
835            assert_eq!(
836                e.to_string(),
837                format!("rendered resource missing {slot}"),
838                "slot {slot} error must be canonical"
839            );
840        }
841    }
842
843    // ─── RenderedResourceCoords::required_str substrate pins ────────
844    //
845    // Fail-before-pass-after granularity: the
846    // `RenderedResourceCoords::required_str` inherent associated
847    // function did not exist before this commit, so each test below
848    // fails to compile pre-lift. Post-lift they collectively pin the
849    // required-string-extract shape at ONE substrate owner — a
850    // regression that swaps `MISSING_MESSAGE_PREFIX`, decouples the
851    // `key` / `error_slot` slot pair with a wrong ordering, drops the
852    // `str::to_string` coerce (returning `&str` and forcing every
853    // consumer to re-stamp `.to_string()` per site), or narrows the
854    // receiver from `Option<&Value>` to `&Value` (silently breaking
855    // the `metadata.name` corner where the caller threads the
856    // `res.get("metadata")` result directly) surfaces HERE rather
857    // than as silent operator-facing skew across the three pre-lift
858    // consumers on `from_json`.
859
860    #[test]
861    fn required_str_present_string_slot_returns_owned_string() {
862        // Ok-arm invariant: a present string slot at `key` on a
863        // `Some(&Value::Object)` receiver returns `Ok(<owned>)` —
864        // the primitive absorbs the `.to_string()` coerce the three
865        // pre-lift restatements each stamped at the tail.
866        let res = json!({"apiVersion": "kustomize.toolkit.fluxcd.io/v1"});
867        let got =
868            RenderedResourceCoords::required_str(Some(&res), "apiVersion", "apiVersion").unwrap();
869        assert_eq!(got, "kustomize.toolkit.fluxcd.io/v1");
870    }
871
872    #[test]
873    fn required_str_none_receiver_errors_with_canonical_wire_form() {
874        // Absent-shape corner 1: the caller threads `None`
875        // (`res.get("metadata")` returned `None` because the top-
876        // level `metadata` slot itself is absent). The primitive
877        // errors with the SAME wire form the two other absent
878        // corners produce, keeping the operator-facing footprint
879        // singular.
880        let e = RenderedResourceCoords::required_str(None, "name", "metadata.name")
881            .expect_err("None receiver must error");
882        assert_eq!(e.to_string(), "rendered resource missing metadata.name");
883    }
884
885    #[test]
886    fn required_str_absent_slot_errors_with_canonical_wire_form() {
887        // Absent-shape corner 2: the receiver is present but the
888        // slot at `key` is not stamped on it. Wire form matches
889        // the `None`-receiver corner and the non-string corner.
890        let res = json!({"kind": "K"});
891        let e = RenderedResourceCoords::required_str(Some(&res), "apiVersion", "apiVersion")
892            .expect_err("absent slot must error");
893        assert_eq!(e.to_string(), "rendered resource missing apiVersion");
894    }
895
896    #[test]
897    fn required_str_non_string_slot_errors_with_canonical_wire_form() {
898        // Absent-shape corner 3: the slot is present but stamped
899        // as a JSON number / bool / object / array — every
900        // non-`Value::String` variant falls through the underlying
901        // `get_str` gate and produces the SAME `"missing"` diagnostic.
902        // Pinning EVERY non-string variant here (not just number)
903        // guarantees an operator's error-stream grep collapses all
904        // fixture-authoring bugs at this slot onto one footprint.
905        for bad in [
906            json!({"apiVersion": 42}),
907            json!({"apiVersion": true}),
908            json!({"apiVersion": {}}),
909            json!({"apiVersion": [1]}),
910            json!({"apiVersion": null}),
911        ] {
912            let e = RenderedResourceCoords::required_str(Some(&bad), "apiVersion", "apiVersion")
913                .expect_err("non-string slot must error");
914            assert_eq!(e.to_string(), "rendered resource missing apiVersion");
915        }
916    }
917
918    #[test]
919    fn required_str_non_object_receiver_errors_with_canonical_wire_form() {
920        // Absent-shape corner 4: the receiver itself is not a
921        // `Value::Object` — a resource authored as a JSON array,
922        // string, or null at any of the levels the primitive
923        // walks. The underlying `get_str` step returns `None`
924        // verbatim (matching the pre-lift chain's own behavior)
925        // and the primitive stamps the canonical wire form.
926        for bad in [json!([1, 2, 3]), json!("stringified"), Value::Null] {
927            let e = RenderedResourceCoords::required_str(Some(&bad), "name", "metadata.name")
928                .expect_err("non-object receiver must error");
929            assert_eq!(e.to_string(), "rendered resource missing metadata.name");
930        }
931    }
932
933    #[test]
934    fn required_str_decouples_key_from_error_slot_at_metadata_name_shape() {
935        // Slot-decoupling pin: for the `metadata.name` corner the
936        // primitive looks up `key = "name"` on the metadata sub-
937        // object while stamping `error_slot = "metadata.name"` into
938        // the error's `Display` output — the two are NOT the same
939        // string, and a regression that collapsed them (using
940        // `key` for both the lookup AND the error slug, or
941        // vice-versa) would silently pass the shallow `apiVersion`
942        // / `kind` pins above (where `key == error_slot`) and fail
943        // HERE. Present-arm: lookup succeeds on the metadata sub-
944        // object's `name` slot, returns the owned string.
945        let res = json!({"metadata": {"name": "demo"}});
946        let metadata = res.get("metadata");
947        let got = RenderedResourceCoords::required_str(metadata, "name", "metadata.name").unwrap();
948        assert_eq!(got, "demo");
949        // Absent-arm: same slot-decoupling but the `name` sub-slot
950        // is absent — the error slug is the DOTTED path, not the
951        // shallow `"name"` key.
952        let res_no_name = json!({"metadata": {}});
953        let metadata_empty = res_no_name.get("metadata");
954        let e = RenderedResourceCoords::required_str(metadata_empty, "name", "metadata.name")
955            .expect_err("absent metadata.name must error");
956        assert_eq!(e.to_string(), "rendered resource missing metadata.name");
957    }
958
959    #[test]
960    fn required_str_error_wire_form_composes_missing_message_prefix_verbatim() {
961        // Wire-form composition pin: the error's `Display` is
962        // exactly `"<Self::MISSING_MESSAGE_PREFIX> <error_slot>"` —
963        // the leading prefix comes from the `const` owner + a
964        // single space + the caller-supplied slug. A regression
965        // that switched the separator (a colon, an em-dash) or
966        // dropped the prefix (returning just the slot slug) would
967        // silently invert every operator-facing log grep footprint;
968        // this pin binds the composition to the ONE prefix const
969        // so a future rename lands atomically at both the source
970        // and the pins.
971        let e = RenderedResourceCoords::required_str(None, "name", "metadata.name")
972            .expect_err("None receiver must error");
973        let expected = format!(
974            "{prefix} metadata.name",
975            prefix = RenderedResourceCoords::MISSING_MESSAGE_PREFIX,
976        );
977        assert_eq!(e.to_string(), expected);
978    }
979
980    #[test]
981    fn required_str_shape_parity_matches_pre_lift_hand_authored_chain_bytewise() {
982        // Byte-shape parity pin: on every corner (present, absent,
983        // non-string, non-object, None-receiver) the primitive's
984        // output MUST match the pre-lift hand-authored
985        // `.get_str(<key>).ok_or_else(|| anyhow!("rendered resource
986        // missing <slot>"))?.to_string()` chain bytewise — the
987        // Ok-arm string equals the raw `get_str` slice as an owned
988        // `String`, and the Err-arm `Display` equals the pre-lift
989        // `anyhow!(...)` output verbatim. A regression that inserted
990        // a normalization (a trim, an NFC-fold) into the Ok arm or
991        // altered the diagnostic wrapping in the Err arm surfaces
992        // HERE rather than as silent per-consumer schema drift.
993        let cases: &[(Value, &'static str, &'static str)] = &[
994            (json!({"apiVersion": "v1"}), "apiVersion", "apiVersion"),
995            (json!({"kind": "K"}), "kind", "kind"),
996        ];
997        for (res, key, error_slot) in cases {
998            let via_primitive =
999                RenderedResourceCoords::required_str(Some(res), key, error_slot).unwrap();
1000            let via_pre_lift = res.get_str(key).unwrap().to_string();
1001            assert_eq!(via_primitive, via_pre_lift);
1002        }
1003        let empty = json!({"other": "value"});
1004        let err_via_primitive =
1005            RenderedResourceCoords::required_str(Some(&empty), "apiVersion", "apiVersion")
1006                .expect_err("absent slot must error");
1007        let err_via_pre_lift = anyhow::anyhow!("rendered resource missing apiVersion");
1008        assert_eq!(err_via_primitive.to_string(), err_via_pre_lift.to_string());
1009    }
1010
1011    #[test]
1012    fn required_str_missing_message_prefix_matches_pre_lift_wire_form_verbatim() {
1013        // Const-owner pin: the pre-lift hand-authored `anyhow!("rendered
1014        // resource missing X")` restatements each embedded the leading
1015        // `"rendered resource missing"` prefix as an inline literal.
1016        // Post-lift the prefix lives at ONE const owner — a rename lands
1017        // there and the three consumers on `from_json` inherit the
1018        // rename mechanically. This pin binds the const to the pre-lift
1019        // spelling so a rename shows up at BOTH the const definition
1020        // AND this pin as a coherent atomic edit, not as a silent
1021        // diff between the const and its downstream consumers.
1022        assert_eq!(
1023            RenderedResourceCoords::MISSING_MESSAGE_PREFIX,
1024            "rendered resource missing",
1025        );
1026    }
1027
1028    #[test]
1029    fn rendered_resource_coords_namespace_or_default_returns_slice_when_some() {
1030        let c = RenderedResourceCoords {
1031            api_version: "v1".into(),
1032            kind: "K".into(),
1033            name: "n".into(),
1034            namespace: Some("prod".into()),
1035        };
1036        assert_eq!(c.namespace_or_default(), "prod");
1037    }
1038
1039    #[test]
1040    fn rendered_resource_coords_namespace_or_default_falls_back_when_none() {
1041        let c = RenderedResourceCoords {
1042            api_version: "v1".into(),
1043            kind: "K".into(),
1044            name: "n".into(),
1045            namespace: None,
1046        };
1047        assert_eq!(c.namespace_or_default(), Process::DEFAULT_NAMESPACE);
1048        assert_eq!(c.namespace_or_default(), "default");
1049    }
1050
1051    // ─── FluxResourceRef::fetch_coords substrate pins ─────────────
1052    //
1053    // The 4-slot `(&namespace, &api_version, &kind, &name)` borrow
1054    // projection lifts the pre-existing 5-slot `ssapply::fetch(client,
1055    // &r.namespace, &r.api_version, &r.kind, &r.name)` splat that
1056    // recurred at TWO hand-authored sites in
1057    // `tatara-reconciler::phase_machine` (`handle_running`,
1058    // `handle_attested`) past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
1059    // trigger. These pins bind the slot order at fail-before-pass-
1060    // after granularity so a regression that swapped `namespace` and
1061    // `api_version` (both `String`, mechanically interchangeable to
1062    // a bad refactor) surfaces HERE rather than as a silent wire-time
1063    // 404 at every downstream Flux fetch consumer.
1064
1065    fn sample_flux_ref() -> FluxResourceRef {
1066        // Slot values are deliberately distinct so a swap between any
1067        // two adjacent tuple positions surfaces as an equality
1068        // failure at the assertion site — a slot-inversion regression
1069        // cannot masquerade as identity by accident.
1070        FluxResourceRef {
1071            api_version: "kustomize.toolkit.fluxcd.io/v1".to_string(),
1072            kind: "Kustomization".to_string(),
1073            name: "observability-stack".to_string(),
1074            namespace: "flux-system".to_string(),
1075            ready: true,
1076            message: None,
1077            last_check: None,
1078        }
1079    }
1080
1081    #[test]
1082    fn flux_resource_ref_fetch_coords_binds_slots_by_position() {
1083        // Positional pin: the 4-tuple return binds
1084        // `(namespace, api_version, kind, name)` in THAT order,
1085        // matching the raw `ssapply::fetch(client, ns, av, kind,
1086        // name)` positional signature every pre-lift callsite splatted
1087        // into. A regression that swapped ANY pair of adjacent slots
1088        // (all four axes are `String` and mechanically
1089        // indistinguishable at the type level) would surface here
1090        // rather than as an operator-visible wire-form 404 at every
1091        // downstream fetch consumer.
1092        let r = sample_flux_ref();
1093        let (ns, av, kind, name) = r.fetch_coords();
1094        assert_eq!(ns, "flux-system", "position 0 must be namespace");
1095        assert_eq!(
1096            av, "kustomize.toolkit.fluxcd.io/v1",
1097            "position 1 must be api_version"
1098        );
1099        assert_eq!(kind, "Kustomization", "position 2 must be kind");
1100        assert_eq!(name, "observability-stack", "position 3 must be name");
1101    }
1102
1103    #[test]
1104    fn flux_resource_ref_fetch_coords_returns_borrows_of_owned_slots() {
1105        // Borrow-discipline pin: the 4-tuple returns `&str` borrows
1106        // of the enclosing `FluxResourceRef`'s owned `String` slots —
1107        // NOT a fresh allocation or a clone. A regression that
1108        // switched the projection to owned strings (via `.clone()` or
1109        // `format!`) would defeat the zero-copy contract and would
1110        // surface here via pointer-identity comparison.
1111        let r = sample_flux_ref();
1112        let (ns, av, kind, name) = r.fetch_coords();
1113        assert!(std::ptr::eq(ns.as_ptr(), r.namespace.as_ptr()));
1114        assert!(std::ptr::eq(av.as_ptr(), r.api_version.as_ptr()));
1115        assert!(std::ptr::eq(kind.as_ptr(), r.kind.as_ptr()));
1116        assert!(std::ptr::eq(name.as_ptr(), r.name.as_ptr()));
1117    }
1118
1119    #[test]
1120    fn flux_resource_ref_fetch_coords_is_a_pure_borrow_projection() {
1121        // Purity pin: calling the projection twice on the same ref
1122        // returns byte-identical slices (same pointer, same length).
1123        // A regression that introduced state — a lazy-cached slot
1124        // computed on first call, a normalization step that ran once
1125        // and cached — would surface here rather than as silent drift
1126        // between the VERIFY-phase and ATTEST-heartbeat consumers on
1127        // the SAME ref within one reconcile pass.
1128        let r = sample_flux_ref();
1129        let a = r.fetch_coords();
1130        let b = r.fetch_coords();
1131        assert!(std::ptr::eq(a.0.as_ptr(), b.0.as_ptr()));
1132        assert!(std::ptr::eq(a.1.as_ptr(), b.1.as_ptr()));
1133        assert!(std::ptr::eq(a.2.as_ptr(), b.2.as_ptr()));
1134        assert!(std::ptr::eq(a.3.as_ptr(), b.3.as_ptr()));
1135    }
1136
1137    #[test]
1138    fn flux_resource_ref_fetch_coords_ignores_status_slots() {
1139        // Coverage pin: the projection exposes ONLY the four API-path
1140        // slots the fetch call requires; the ref's status slots
1141        // (`ready`, `message`, `last_check`) are deliberately absent
1142        // from the tuple. The fetch signature admits four `&str`
1143        // slots, and the projection carries EXACTLY those four — no
1144        // silent widening that would surface as an arity mismatch at
1145        // every downstream `fetch(...)` call.
1146        let r = sample_flux_ref();
1147        let coords = r.fetch_coords();
1148        assert_eq!(
1149            std::mem::size_of_val(&coords),
1150            std::mem::size_of::<(&str, &str, &str, &str)>(),
1151            "the 4-tuple width must match the raw fetch signature's four `&str` slots"
1152        );
1153    }
1154
1155    // ─── FluxResourceRef::observed substrate pins ─────────────────
1156    //
1157    // The 6-arg composer stamps `last_check` at ONE substrate site
1158    // (the pre-lift 7-slot struct-literal restated `Some(chrono::
1159    // Utc::now())` at TWO hand-authored sites in
1160    // `tatara-reconciler::phase_machine` — `handle_running`'s per-
1161    // ref VERIFY rebuild and `flux_ref_from_json`'s post-SSA
1162    // seeder). These pins bind the six input slots by position so a
1163    // regression that swapped `api_version` and `kind` (both
1164    // `String`, mechanically interchangeable to a bad refactor)
1165    // surfaces HERE rather than as a silent wire-time 404 at every
1166    // downstream fetch consumer.
1167    //
1168    // Every test constructs distinct values across the four
1169    // `String` coordinate slots so a slot swap fails structurally
1170    // rather than by accident of matching literals.
1171
1172    #[test]
1173    fn flux_resource_ref_observed_binds_slots_by_position() {
1174        // Positional pin: the 6-arg constructor binds
1175        // `(api_version, kind, name, namespace, ready, message)`
1176        // in THAT order, matching the pre-lift 7-slot struct-
1177        // literal's declaration order. A regression that swapped
1178        // ANY pair of adjacent `String` coordinate slots (all four
1179        // are mechanically indistinguishable at the type level)
1180        // would surface here rather than as a wire-time 404 at
1181        // every downstream Flux fetch consumer.
1182        let r = FluxResourceRef::observed(
1183            "kustomize.toolkit.fluxcd.io/v1".to_string(),
1184            "Kustomization".to_string(),
1185            "observability-stack".to_string(),
1186            "flux-system".to_string(),
1187            true,
1188            Some("healthy".to_string()),
1189        );
1190        assert_eq!(r.api_version, "kustomize.toolkit.fluxcd.io/v1");
1191        assert_eq!(r.kind, "Kustomization");
1192        assert_eq!(r.name, "observability-stack");
1193        assert_eq!(r.namespace, "flux-system");
1194        assert!(r.ready);
1195        assert_eq!(r.message.as_deref(), Some("healthy"));
1196    }
1197
1198    #[test]
1199    fn flux_resource_ref_observed_stamps_last_check_at_now() {
1200        // Stamp pin: the `last_check` slot is filled with
1201        // `Some(<recent Utc>)` at the composer's body. A
1202        // regression that dropped the stamp (leaving `None`) or
1203        // shifted it to a stale constant would surface here rather
1204        // than as silent operator-observed staleness at
1205        // `ProcessStatus.flux_resources` panels. Bounds the stamp
1206        // to within a generous 5s window of the composer call so
1207        // slow CI runners do not false-positive.
1208        let before = Utc::now();
1209        let r = FluxResourceRef::observed(
1210            "v1".to_string(),
1211            "K".to_string(),
1212            "n".to_string(),
1213            "ns".to_string(),
1214            false,
1215            None,
1216        );
1217        let after = Utc::now();
1218        let stamp = r.last_check.expect("observed must stamp last_check");
1219        assert!(stamp >= before, "stamp must be >= before-call `now`");
1220        assert!(stamp <= after, "stamp must be <= after-call `now`");
1221    }
1222
1223    #[test]
1224    fn flux_resource_ref_observed_round_trips_through_fetch_coords() {
1225        // Cross-composer coherence pin: a ref built by `observed`
1226        // then unpacked by `fetch_coords` returns the same four
1227        // slots in the peer projection's positional order
1228        // `(namespace, api_version, kind, name)`. Composition of
1229        // the two primitives on the same ref preserves the slot
1230        // identity — a regression at either end (a slot swap in
1231        // `observed`, or a slot swap in `fetch_coords`) would
1232        // surface here rather than as silent drift between the
1233        // writer and the reader on the same persisted slice.
1234        let r = FluxResourceRef::observed(
1235            "helm.toolkit.fluxcd.io/v2".to_string(),
1236            "HelmRelease".to_string(),
1237            "prometheus-op".to_string(),
1238            "monitoring".to_string(),
1239            false,
1240            Some("applied; awaiting reconciliation".to_string()),
1241        );
1242        let (ns, av, kind, name) = r.fetch_coords();
1243        assert_eq!(ns, "monitoring");
1244        assert_eq!(av, "helm.toolkit.fluxcd.io/v2");
1245        assert_eq!(kind, "HelmRelease");
1246        assert_eq!(name, "prometheus-op");
1247    }
1248
1249    #[test]
1250    fn flux_resource_ref_observed_matches_pre_lift_struct_literal_field_for_field() {
1251        // Byte-for-byte parity pin against the pre-lift 7-slot
1252        // struct-literal spelled at BOTH `phase_machine::
1253        // handle_running` and `phase_machine::flux_ref_from_json`.
1254        // A regression that reordered any of the six inputs at
1255        // the composer's argument list, or that swapped a
1256        // `ready`/`message` pair inside the composer's body,
1257        // would surface here rather than as silent divergence
1258        // between the composer's output and the pre-lift hand-
1259        // authored shape every persisted status writer restated.
1260        let composed = FluxResourceRef::observed(
1261            "source.toolkit.fluxcd.io/v1beta2".to_string(),
1262            "OCIRepository".to_string(),
1263            "chart-source".to_string(),
1264            "flux-system".to_string(),
1265            false,
1266            Some("applied; awaiting reconciliation".to_string()),
1267        );
1268        // Hand-authored the same seven slots directly, with a
1269        // held-open stamp window across the composer call.
1270        let stamped = composed.last_check.expect("stamped");
1271        let baseline = FluxResourceRef {
1272            api_version: "source.toolkit.fluxcd.io/v1beta2".to_string(),
1273            kind: "OCIRepository".to_string(),
1274            name: "chart-source".to_string(),
1275            namespace: "flux-system".to_string(),
1276            ready: false,
1277            message: Some("applied; awaiting reconciliation".to_string()),
1278            last_check: Some(stamped),
1279        };
1280        assert_eq!(composed.api_version, baseline.api_version);
1281        assert_eq!(composed.kind, baseline.kind);
1282        assert_eq!(composed.name, baseline.name);
1283        assert_eq!(composed.namespace, baseline.namespace);
1284        assert_eq!(composed.ready, baseline.ready);
1285        assert_eq!(composed.message, baseline.message);
1286        assert_eq!(composed.last_check, baseline.last_check);
1287    }
1288
1289    // ─── FluxResourceRef::pending substrate pins ─────────────────────
1290    //
1291    // Bind [`FluxResourceRef::pending`] at fail-before-pass-after
1292    // granularity so a regression that leaked a non-default status
1293    // slot (`ready: true`, `message: Some("something")`, `last_check:
1294    // Some(Utc::now())`), swapped two adjacent coordinate slots (all
1295    // four are `String` and mechanically interchangeable at the type
1296    // level), or diverged from the pre-lift 7-slot struct-literal on
1297    // any of the seven fields surfaces HERE rather than as silent
1298    // operator-invisible drift at the 3 downstream fixture consumers
1299    // (crd.rs `sample_flux_ref`, ssapply.rs `sample_flux_ref_for_diag`,
1300    // ssapply.rs `flux_ref_fetch_error_context_matches_pre_lift_...`).
1301    //
1302    // Each pin is fail-before-pass-after: the primitive did not exist
1303    // pre-lift, so any test that invokes it fails to compile pre-lift
1304    // and passes post-lift; the byte-identity pins below then bind
1305    // the specific shape choice.
1306
1307    #[test]
1308    fn flux_resource_ref_pending_binds_coordinate_slots_by_position() {
1309        // Positional pin: the 4-arg constructor binds `(api_version,
1310        // kind, name, namespace)` in THAT order, matching the pre-
1311        // lift 7-slot struct-literal's declaration order. A regression
1312        // that swapped ANY pair of adjacent `String` coordinate slots
1313        // (all four are mechanically indistinguishable at the type
1314        // level) would surface here rather than as a wire-time 404 at
1315        // every downstream Flux fetch consumer that walks
1316        // `FluxResourceRef.fetch_coords`.
1317        let r = FluxResourceRef::pending(
1318            "kustomize.toolkit.fluxcd.io/v1",
1319            "Kustomization",
1320            "observability-stack",
1321            "flux-system",
1322        );
1323        assert_eq!(r.api_version, "kustomize.toolkit.fluxcd.io/v1");
1324        assert_eq!(r.kind, "Kustomization");
1325        assert_eq!(r.name, "observability-stack");
1326        assert_eq!(r.namespace, "flux-system");
1327    }
1328
1329    #[test]
1330    fn flux_resource_ref_pending_defaults_every_status_slot() {
1331        // Default-slot pin: the three status slots (`ready`, `message`,
1332        // `last_check`) are ALL defaulted at the composer's body — no
1333        // wall-clock read, no non-`None` `message` leak, no `ready:
1334        // true` regression that would silently un-pend the fixture.
1335        // A regression that stamped `Some(Utc::now())` into
1336        // `last_check` (matching the sibling `observed` composer's
1337        // wall-clock read) would silently defeat the deterministic-
1338        // fixture contract the peer partition holds.
1339        let r = FluxResourceRef::pending("v1", "K", "n", "ns");
1340        assert!(
1341            !r.ready,
1342            "pending composer must default `ready` to false — non-`false` breaks the pre-observation contract"
1343        );
1344        assert_eq!(
1345            r.message, None,
1346            "pending composer must default `message` to None — non-`None` leaks a stale message into the pre-observation seed",
1347        );
1348        assert_eq!(
1349            r.last_check, None,
1350            "pending composer must default `last_check` to None — a `Some(_)` leak defeats the deterministic-peer partition against `observed`",
1351        );
1352    }
1353
1354    #[test]
1355    fn flux_resource_ref_pending_accepts_both_owned_and_borrowed_coordinates() {
1356        // The `impl Into<String>` ergonomic contract: both `&'static
1357        // str` literals (the fixture-helper sites that spell
1358        // coordinates inline) and owned `String` (a future callsite
1359        // handing off a dynamically-derived coordinate) round-trip
1360        // through the SAME composer signature without widening. A
1361        // regression that specialised the signature to one form or
1362        // the other would break either the inline-literal helpers or
1363        // the owned-`String` downstream consumers.
1364        let borrowed: FluxResourceRef = FluxResourceRef::pending("v1", "K", "n", "ns");
1365        let owned: FluxResourceRef = FluxResourceRef::pending(
1366            "v1".to_string(),
1367            "K".to_string(),
1368            "n".to_string(),
1369            "ns".to_string(),
1370        );
1371        assert_eq!(borrowed.api_version, owned.api_version);
1372        assert_eq!(borrowed.kind, owned.kind);
1373        assert_eq!(borrowed.name, owned.name);
1374        assert_eq!(borrowed.namespace, owned.namespace);
1375        assert_eq!(borrowed.ready, owned.ready);
1376        assert_eq!(borrowed.message, owned.message);
1377        assert_eq!(borrowed.last_check, owned.last_check);
1378    }
1379
1380    #[test]
1381    fn flux_resource_ref_pending_matches_pre_lift_struct_literal_bytewise() {
1382        // Byte-for-byte parity pin against the pre-lift 7-slot
1383        // struct-literal spelled at ALL THREE hand-authored fixture
1384        // sites (crd.rs `sample_flux_ref`, ssapply.rs
1385        // `sample_flux_ref_for_diag`, ssapply.rs inline in the
1386        // cross-substrate coherence pin's per-case sweep). Sweeps the
1387        // three representative coordinate tuples the pre-lift sites
1388        // used, so a regression that special-cased any one variant
1389        // (a `Kustomization`-only path via `if kind ==
1390        // "Kustomization" ...`) surfaces here.
1391        let cases = [
1392            (
1393                "kustomize.toolkit.fluxcd.io/v1",
1394                "Kustomization",
1395                "observability-stack",
1396                "flux-system",
1397            ),
1398            (
1399                "helm.toolkit.fluxcd.io/v2",
1400                "HelmRelease",
1401                "prometheus-op",
1402                "monitoring",
1403            ),
1404            (
1405                "source.toolkit.fluxcd.io/v1beta2",
1406                "OCIRepository",
1407                "chart-source",
1408                "flux-system",
1409            ),
1410        ];
1411        for (av, kind, name, ns) in cases {
1412            let composed = FluxResourceRef::pending(av, kind, name, ns);
1413            let hand_authored = FluxResourceRef {
1414                api_version: av.to_string(),
1415                kind: kind.to_string(),
1416                name: name.to_string(),
1417                namespace: ns.to_string(),
1418                ready: false,
1419                message: None,
1420                last_check: None,
1421            };
1422            assert_eq!(composed.api_version, hand_authored.api_version);
1423            assert_eq!(composed.kind, hand_authored.kind);
1424            assert_eq!(composed.name, hand_authored.name);
1425            assert_eq!(composed.namespace, hand_authored.namespace);
1426            assert_eq!(composed.ready, hand_authored.ready);
1427            assert_eq!(composed.message, hand_authored.message);
1428            assert_eq!(composed.last_check, hand_authored.last_check);
1429        }
1430    }
1431
1432    #[test]
1433    fn flux_resource_ref_pending_partitions_the_composer_axis_against_observed() {
1434        // Cross-composer partition pin: `pending` and `observed`
1435        // both produce `FluxResourceRef` but partition the composer
1436        // axis at the (deterministic-fixture, wall-clock-observed)
1437        // split — `pending` reads no clock and leaves `last_check:
1438        // None`, `observed` reads the wall clock and stamps
1439        // `last_check: Some(<recent Utc>)`. A regression that merged
1440        // either primitive onto the other (a `pending` that started
1441        // stamping `Utc::now()`, an `observed` that started leaving
1442        // `last_check: None`) would collapse the partition and
1443        // surface here.
1444        let p = FluxResourceRef::pending("v1", "K", "n", "ns");
1445        assert_eq!(
1446            p.last_check, None,
1447            "pending is deterministic — no clock read"
1448        );
1449        let o = FluxResourceRef::observed(
1450            "v1".to_string(),
1451            "K".to_string(),
1452            "n".to_string(),
1453            "ns".to_string(),
1454            false,
1455            None,
1456        );
1457        assert!(o.last_check.is_some(), "observed reads the wall clock");
1458    }
1459
1460    #[test]
1461    fn flux_resource_ref_pending_composes_with_fetch_coords_at_pre_observation_shape() {
1462        // Cross-composer coherence pin: a ref built by `pending`
1463        // then unpacked by `fetch_coords` returns the same four
1464        // slots in the peer projection's positional order
1465        // `(namespace, api_version, kind, name)`. Composition of
1466        // the two primitives on the same pre-observation ref
1467        // preserves the slot identity — a regression at either end
1468        // (a slot swap in `pending`, or a slot swap in
1469        // `fetch_coords`) would surface here rather than as silent
1470        // drift between the fixture writer and every downstream
1471        // fetch reader.
1472        let r = FluxResourceRef::pending(
1473            "helm.toolkit.fluxcd.io/v2",
1474            "HelmRelease",
1475            "prometheus-op",
1476            "monitoring",
1477        );
1478        let (ns, av, kind, name) = r.fetch_coords();
1479        assert_eq!(ns, "monitoring");
1480        assert_eq!(av, "helm.toolkit.fluxcd.io/v2");
1481        assert_eq!(kind, "HelmRelease");
1482        assert_eq!(name, "prometheus-op");
1483    }
1484
1485    #[test]
1486    fn rendered_resource_coords_namespace_fallback_shares_process_default_const() {
1487        // Byte-identity between the namespace fallback and the workspace-
1488        // wide `Process::DEFAULT_NAMESPACE` const. A regression that spelled
1489        // the fallback as any other string ("kube-system", "", "default-ns")
1490        // would silently drift between the coord-primitive family here and
1491        // the `Process`-borne family in `crd.rs` — surfaces here rather than
1492        // as operator-observed namespace routing skew between the two
1493        // primitive families.
1494        let c = RenderedResourceCoords {
1495            api_version: "v1".into(),
1496            kind: "K".into(),
1497            name: "n".into(),
1498            namespace: None,
1499        };
1500        assert_eq!(c.namespace_or_default(), Process::DEFAULT_NAMESPACE);
1501    }
1502
1503    // ─── CheckedCondition::all_satisfied substrate pins ─────────────
1504    //
1505    // Bind [`CheckedCondition::all_satisfied`] at fail-before-pass-
1506    // after granularity so a regression that flipped the fold
1507    // direction (`any` for `all`), inverted the projected bit
1508    // (`!c.satisfied`), reshaped the return form (an owned
1509    // `Vec<bool>` instead of the folded `bool`), or dropped the
1510    // vacuous-truth empty-slice corner surfaces HERE rather than as
1511    // silent operator-facing gate-flip at the reconciler's PROVE-
1512    // phase precondition gate + VERIFY-phase postcondition gate.
1513
1514    fn sample_checked(satisfied: bool) -> CheckedCondition {
1515        CheckedCondition {
1516            condition: crate::boundary::Condition {
1517                kind: crate::boundary::ConditionKind::ProcessPhase,
1518                params: serde_json::json!({}),
1519            },
1520            satisfied,
1521            last_check: None,
1522            message: None,
1523        }
1524    }
1525
1526    #[test]
1527    fn checked_condition_all_satisfied_returns_true_when_every_row_is_satisfied() {
1528        // Populated slice, every row `satisfied = true` — the RENDER-
1529        // phase advance corner: `handle_execing` proceeds to intent
1530        // dispatch iff every precondition holds.
1531        let checked = vec![
1532            sample_checked(true),
1533            sample_checked(true),
1534            sample_checked(true),
1535        ];
1536        assert!(
1537            CheckedCondition::all_satisfied(&checked),
1538            "all-satisfied slice must fold to true — a regression that inverted the bit would silently gate every RENDER advance behind an inverted predicate"
1539        );
1540    }
1541
1542    #[test]
1543    fn checked_condition_all_satisfied_returns_false_when_any_row_is_unsatisfied() {
1544        // Populated slice with ONE unsatisfied row — the heartbeat
1545        // requeue corner: `handle_running` stays in Running while any
1546        // postcondition remains unsatisfied.
1547        let mixed = vec![
1548            sample_checked(true),
1549            sample_checked(false),
1550            sample_checked(true),
1551        ];
1552        assert!(
1553            !CheckedCondition::all_satisfied(&mixed),
1554            "mixed slice must fold to false — a regression that folded via `any` instead of `all` would silently green-light every VERIFY advance"
1555        );
1556    }
1557
1558    #[test]
1559    fn checked_condition_all_satisfied_returns_false_when_every_row_is_unsatisfied() {
1560        // Populated slice with EVERY row unsatisfied — the tightest
1561        // gate corner: no phase advance is legal.
1562        let none_pass = vec![sample_checked(false), sample_checked(false)];
1563        assert!(
1564            !CheckedCondition::all_satisfied(&none_pass),
1565            "all-unsatisfied slice must fold to false"
1566        );
1567    }
1568
1569    #[test]
1570    fn checked_condition_all_satisfied_returns_true_on_empty_slice() {
1571        // Empty-slice vacuous-truth corner: `[T]::iter().all(_)`
1572        // returns `true` on empty input, and the pre-lift phase
1573        // gate's `if !preconditions.is_empty() { ... }` guard sat
1574        // BEFORE the fold, so the fold itself never saw an empty
1575        // slice in production. Post-lift the primitive absorbs the
1576        // empty corner cleanly — a caller that drops the outer
1577        // `is_empty()` guard (a future path that folds every gate
1578        // through this ONE primitive without a prior gate) still
1579        // sees the vacuous-truth semantics that match
1580        // [`Iterator::all`].
1581        let empty: Vec<CheckedCondition> = vec![];
1582        assert!(
1583            CheckedCondition::all_satisfied(&empty),
1584            "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"
1585        );
1586    }
1587
1588    #[test]
1589    fn checked_condition_all_satisfied_matches_pre_lift_iter_all_chain_shape() {
1590        // Byte-identity pin against the pre-lift `.iter().all(|c|
1591        // c.satisfied)` chain shape the reconciler's two boundary
1592        // gates hand-authored. Sweeps every corner every gate
1593        // plausibly encounters (empty slice, single satisfied,
1594        // single unsatisfied, mixed satisfied first, mixed
1595        // unsatisfied first) so a regression that reshaped either
1596        // link surfaces HERE rather than at the two downstream phase
1597        // gates.
1598        let corners: Vec<Vec<CheckedCondition>> = vec![
1599            vec![],
1600            vec![sample_checked(true)],
1601            vec![sample_checked(false)],
1602            vec![sample_checked(true), sample_checked(false)],
1603            vec![sample_checked(false), sample_checked(true)],
1604            vec![
1605                sample_checked(true),
1606                sample_checked(true),
1607                sample_checked(true),
1608            ],
1609            vec![
1610                sample_checked(false),
1611                sample_checked(false),
1612                sample_checked(false),
1613            ],
1614        ];
1615        for corner in &corners {
1616            let via_primitive = CheckedCondition::all_satisfied(corner);
1617            #[allow(clippy::redundant_closure_for_method_calls)]
1618            let hand_authored = corner.iter().all(|c| c.satisfied);
1619            assert_eq!(
1620                via_primitive, hand_authored,
1621                "all_satisfied fold must match hand-authored .iter().all(|c| c.satisfied) chain byte-identically at corner {corner:?}"
1622            );
1623        }
1624    }
1625
1626    #[test]
1627    fn checked_condition_all_satisfied_short_circuits_on_first_unsatisfied_row() {
1628        // Semantic pin against [`Iterator::all`]'s short-circuit
1629        // discipline: a regression that folded via `checked.iter()
1630        // .filter(|c| c.satisfied).count() == checked.len()` would
1631        // still produce the same `bool` result but would eagerly
1632        // walk every row, and a future addition of an expensive
1633        // per-row side effect (a metric emit, a log line, a
1634        // conditional postcondition-retry hook) would silently fire
1635        // on every row past the first failure. The primitive must
1636        // preserve the pre-lift short-circuit — a regression that
1637        // dropped it would drift telemetry, not correctness, and
1638        // would evade every other pin here. This test verifies
1639        // short-circuit by threading a counter through a peer
1640        // predicate that mirrors [`CheckedCondition::satisfied`]'s
1641        // read.
1642        use std::cell::Cell;
1643        let visited = Cell::new(0_usize);
1644        let checked: Vec<CheckedCondition> = vec![
1645            sample_checked(true),
1646            sample_checked(false),
1647            sample_checked(true),
1648            sample_checked(true),
1649        ];
1650        // Manual short-circuit fold that counts per-row reads —
1651        // must match `all_satisfied`'s count on the same slice.
1652        let via_manual = checked.iter().all(|c| {
1653            visited.set(visited.get() + 1);
1654            c.satisfied
1655        });
1656        let manual_visited = visited.get();
1657        visited.set(0);
1658        // Mirror the primitive's iteration by re-running the same
1659        // fold shape and confirming the visited count matches — the
1660        // primitive itself doesn't take a side-effecting closure,
1661        // but this pin confirms the semantic shape (2 visits on
1662        // this slice: row 0 satisfied, row 1 unsatisfied, stop).
1663        assert_eq!(via_manual, CheckedCondition::all_satisfied(&checked));
1664        assert_eq!(
1665            manual_visited, 2,
1666            "short-circuit must stop at the first unsatisfied row (index 1); manual fold visited {manual_visited} rows"
1667        );
1668    }
1669}