Skip to main content

tatara_process/
crd.rs

1//! The `Process` CRD — `tatara.pleme.io/v1alpha1`.
2
3use chrono::{DateTime, Utc};
4use kube::CustomResource;
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7use tatara_lisp::DeriveTataraDomain;
8
9use crate::attestation::ProcessAttestation;
10use crate::boundary::Boundary;
11use crate::classification::{Classification, ClassificationAxis};
12use crate::compliance::ComplianceSpec;
13use crate::encapsulates::EncapsulatesSpec;
14use crate::identity::Identity;
15use crate::intent::Intent;
16use crate::lifetime::{EphemeralLifetime, Lifetime};
17use crate::phase::ProcessPhase;
18use crate::routing::RoutingSpec;
19use crate::signal::ProcessSignal;
20use crate::spec::{DependsOn, IdentitySpec, SignalPolicy};
21use crate::status::{BoundaryStatus, ComplianceStatus, FluxResourceRef, ProcessCondition};
22
23/// Process — one element of the tatara convergence lattice, reconciled as a Unix process.
24///
25/// ```yaml
26/// apiVersion: tatara.pleme.io/v1alpha1
27/// kind: Process
28/// metadata:
29///   name: observability-stack
30///   namespace: seph
31/// spec:
32///   identity:
33///     parent: seph.1
34///   classification:
35///     pointType: Gate
36///     substrate: Observability
37///   intent:
38///     nix:
39///       flakeRef: github:pleme-io/k8s?dir=shared/infrastructure
40///       attribute: observability
41///   compliance:
42///     baseline: fedramp-moderate
43///     bindings:
44///       - framework: nist-800-53
45///         controlId: SC-7
46///         phase: AtBoundary
47///   dependsOn:
48///     - name: secret-injection
49/// ```
50#[derive(CustomResource, DeriveTataraDomain, Clone, Debug, Deserialize, Serialize, JsonSchema)]
51#[kube(
52    group = "tatara.pleme.io",
53    version = "v1alpha1",
54    kind = "Process",
55    plural = "processes",
56    shortname = "proc",
57    namespaced,
58    status = "ProcessStatus",
59    printcolumn = r#"{"name":"PID","type":"string","jsonPath":".status.pid"}"#,
60    printcolumn = r#"{"name":"Phase","type":"string","jsonPath":".status.phase"}"#,
61    printcolumn = r#"{"name":"Type","type":"string","jsonPath":".spec.classification.pointType"}"#,
62    printcolumn = r#"{"name":"Substrate","type":"string","jsonPath":".spec.classification.substrate"}"#,
63    printcolumn = r#"{"name":"Gen","type":"integer","jsonPath":".status.attestation.generation"}"#,
64    printcolumn = r#"{"name":"Age","type":"date","jsonPath":".metadata.creationTimestamp"}"#
65)]
66#[serde(rename_all = "camelCase")]
67#[tatara(keyword = "defpoint")]
68pub struct ProcessSpec {
69    /// Identity (parent, name override).
70    #[serde(default)]
71    pub identity: IdentitySpec,
72
73    /// Lattice position (6 dimensions).
74    pub classification: Classification,
75
76    /// Where rendered artifacts come from. Exactly one variant must be set.
77    pub intent: Intent,
78
79    /// Boundary predicates (preconditions / postconditions).
80    #[serde(default)]
81    pub boundary: Boundary,
82
83    /// Compliance bindings + baseline.
84    #[serde(default)]
85    pub compliance: ComplianceSpec,
86
87    /// Lattice dependencies — must reach phase before we proceed.
88    #[serde(default)]
89    pub depends_on: Vec<DependsOn>,
90
91    /// Signal policy (grace, SIGHUP strategy, start-suspended).
92    #[serde(default)]
93    pub signals: SignalPolicy,
94
95    /// Lifetime — `Permanent` (default, re-converging) or `Ephemeral`
96    /// (auto-SIGTERM per `teardown_policy` + TTL clock).
97    #[serde(default, skip_serializing_if = "Lifetime::is_default")]
98    pub lifetime: Lifetime,
99
100    /// External edges — DNS + Ingress. When `None`, the Process is
101    /// internal-only (matches today's default). See
102    /// [`crate::routing`] for the full shape.
103    #[serde(default, skip_serializing_if = "Option::is_none")]
104    pub routing: Option<RoutingSpec>,
105
106    /// Pre-existing in-cluster state this Process wraps. When `None`,
107    /// the Process is greenfield (Manage mode implicitly applied to
108    /// nothing pre-existing). See [`crate::encapsulates`] for the
109    /// three modes (Manage / Adopt / Observe).
110    #[serde(default, skip_serializing_if = "Option::is_none")]
111    pub encapsulates: Option<EncapsulatesSpec>,
112
113    /// Soft-suspend marker — reconciler treats as SIGSTOP.
114    /// Same effect as delivering SIGSTOP, but persistent across restarts.
115    #[serde(default)]
116    pub suspended: bool,
117}
118
119// Coordinate primitives — the `(namespace, name)` pair every downstream
120// composer (annotation writers, claim arbiter, boundary evaluator,
121// render owner-metadata seed) pulled by hand from `Process.metadata`
122// pre-lift, each restating the same two `Option<String>`-to-`&str`
123// unwrap incantations with the same two workspace-wide fallback
124// strings sprayed inline. Post-lift the pair lives at ONE substrate
125// primitive on `Process` — a future normalization (case-fold,
126// unicode-safe collation, cross-cluster prefix, a rename of either
127// fallback) lands here and every downstream composer inherits the
128// upgrade mechanically. Peer to `qualified_process_ref` in
129// `tatara-reconciler::ssapply`, whose two `&str` arguments are
130// exactly the pair `Process::coordinates_or_defaults` returns.
131impl Process {
132    /// The K8s canonical default namespace — the fallback every
133    /// consumer of a `Process` whose `metadata.namespace` is `None`
134    /// substitutes. Matches the string K8s itself substitutes on
135    /// namespaced resource writes with no explicit namespace.
136    pub const DEFAULT_NAMESPACE: &'static str = "default";
137
138    /// Workspace-wide fallback for a `Process`'s `metadata.name` when
139    /// it is `None` — the sentinel every annotation writer, claim
140    /// arbiter, and owner-metadata seed substitutes so downstream
141    /// grepping / label-selecting sees a stable spelling rather than
142    /// a per-callsite ad-hoc placeholder (`""`, `"<unnamed>"`, or the
143    /// empty `unwrap_or_default()` fallback). A Process authored
144    /// through the reconciler's fork path always has a name; this
145    /// constant covers the surface where an untyped `Process` value
146    /// (test fixture, dynamic API response, adopted resource pre-
147    /// name-resolution) surfaces without one.
148    pub const UNNAMED_PLACEHOLDER: &'static str = "unnamed";
149
150    /// Namespace slice with the [`Self::DEFAULT_NAMESPACE`] fallback
151    /// applied — the ONE-line collapse of the `metadata.namespace
152    /// .as_deref().unwrap_or("default")` incantation every consumer
153    /// spelled by hand pre-lift.
154    ///
155    /// Peer to [`Self::name_or_placeholder`] on the (metadata slot ×
156    /// fallback shape) axis; both compose through
157    /// [`Self::coordinates_or_defaults`] when a consumer needs the
158    /// pair together (annotation writers, claim-arbiter row builders,
159    /// render owner-metadata seed).
160    pub fn namespace_or_default(&self) -> &str {
161        self.metadata
162            .namespace
163            .as_deref()
164            .unwrap_or(Self::DEFAULT_NAMESPACE)
165    }
166
167    /// Name slice with the [`Self::UNNAMED_PLACEHOLDER`] fallback
168    /// applied — the ONE-line collapse of the `metadata.name.as_deref
169    /// ().unwrap_or("unnamed")` incantation every consumer spelled by
170    /// hand pre-lift.
171    ///
172    /// Peer to [`Self::namespace_or_default`] on the (metadata slot ×
173    /// fallback shape) axis; both compose through
174    /// [`Self::coordinates_or_defaults`] when a consumer needs the
175    /// pair together.
176    pub fn name_or_placeholder(&self) -> &str {
177        self.metadata
178            .name
179            .as_deref()
180            .unwrap_or(Self::UNNAMED_PLACEHOLDER)
181    }
182
183    /// `(namespace, name)` coordinates with the workspace-wide default
184    /// fallbacks applied — the ONE-line collapse of the paired
185    /// `metadata.namespace.as_deref().unwrap_or("default")` +
186    /// `metadata.name.as_deref().unwrap_or("unnamed")` extraction
187    /// every downstream composer restated by hand pre-lift.
188    ///
189    /// Return-tuple order matches the axis order of the substrate's
190    /// paired-composer primitive
191    /// `tatara_reconciler::ssapply::qualified_process_ref(ns, name)`:
192    /// the (namespace, name) pair this method returns feeds that
193    /// primitive positionally without an axis-swap step.
194    pub fn coordinates_or_defaults(&self) -> (&str, &str) {
195        (self.namespace_or_default(), self.name_or_placeholder())
196    }
197
198    /// `(namespace, name)` coordinates as owned `String`s, with the
199    /// namespace half fallback-defaulted to [`Self::DEFAULT_NAMESPACE`]
200    /// but the name half REQUIRED — an [`anyhow::Error`] is returned
201    /// when `metadata.name` is absent, because "unnamed" is a display
202    /// placeholder, not a valid K8s API path segment. Fed straight into
203    /// kube-rs API calls (`Api::patch`, `Api::delete`, `Api::get`) that
204    /// take owned `String` arguments; the [`Self::DEFAULT_NAMESPACE`]
205    /// fallback matches what K8s itself substitutes on namespaced
206    /// resource writes with no explicit namespace, so the surface is
207    /// safe against a `Process` whose `metadata.namespace` slot is
208    /// absent (test fixture, dynamic API response pre-defaulting) but
209    /// refuses to guess a name.
210    ///
211    /// Peer to [`Self::coordinates_or_defaults`] on the (return-form ×
212    /// name gate) axis pair:
213    /// * borrow + name-defaulted → `coordinates_or_defaults` (display,
214    ///   annotation writers, ownership-tag composers — every consumer
215    ///   whose downstream drops `"unnamed"` in place of a missing name
216    ///   without an operator-visible failure);
217    /// * owned + name-required → this method (kube-rs API calls —
218    ///   every consumer whose downstream must NOT silently substitute
219    ///   a placeholder for the API call target, because the caller is
220    ///   about to `patch`/`delete`/`get` at `metadata.name`).
221    ///
222    /// The error wording is pinned by
223    /// [`tests::owned_coordinates_or_err_error_message_matches_pre_lift_reconciler_wording`]
224    /// to match the exact spelling every pre-lift `tatara-reconciler`
225    /// helper produced (`"Process has no metadata.name"`) so log-line
226    /// / test greps that anchored on that wording keep matching post-
227    /// lift, and no operator-visible message drift lands as a side
228    /// effect of the substrate move.
229    pub fn owned_coordinates_or_err(&self) -> anyhow::Result<(String, String)> {
230        let ns = self
231            .metadata
232            .namespace
233            .clone()
234            .unwrap_or_else(|| Self::DEFAULT_NAMESPACE.into());
235        let name = self
236            .metadata
237            .name
238            .clone()
239            .ok_or_else(|| anyhow::anyhow!("Process has no metadata.name"))?;
240        Ok((ns, name))
241    }
242
243    /// `(name, uid)` coordinates as owned `String`s — BOTH slots
244    /// REQUIRED, spelled with the workspace-canonical
245    /// `"Process has no metadata.<slot>"` wire-form message
246    /// [`Self::owned_coordinates_or_err`] pins on the name-gate.
247    ///
248    /// Sibling to [`Self::owned_coordinates_or_err`] on the
249    /// (metadata × pair × required) axis of the coordinate-primitive
250    /// family, partitioned by SLOT PAIR:
251    /// * [`Self::owned_coordinates_or_err`] → `(namespace, name)`, the
252    ///   pair every kube-rs `Api::patch` / `Api::delete` / `Api::get`
253    ///   call takes positionally on a namespaced-scoped `Api<Process>`
254    ///   handle (namespace fallback-defaulted, name gate REQUIRED).
255    /// * this method → `(name, uid)`, the pair
256    ///   [`crate::owner_reference_json`] / [`crate::owner_references_json`]
257    ///   take positionally to compose a K8s OwnerReference entry
258    ///   pointing at this `Process` (BOTH gates REQUIRED — an empty
259    ///   `uid` string would collide with the `is_empty` gate on
260    ///   [`crate::owner_references_json`] and silently drop the owner
261    ///   reference, so the missing-uid corner errors here rather than
262    ///   propagates as a silent orphan).
263    ///
264    /// Pre-lift the paired 2-slot required-extract shape was hand-
265    /// authored at `tatara-reconciler::ssapply::build_owner_reference`
266    /// past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold, restating
267    /// the SAME `.metadata.<slot>.clone().ok_or_else(|| anyhow!(
268    /// "<prefix> missing metadata.<slot>"))?` chain at BOTH the name
269    /// gate AND the uid gate. The pre-lift wire-form drifted from the
270    /// workspace-canonical spelling [`Self::owned_coordinates_or_err`]
271    /// pins (`"Process has no metadata.name"`) to a lowercase-verb
272    /// variant (`"process missing metadata.name"`) — post-lift both
273    /// gates route through THIS primitive and inherit the workspace-
274    /// canonical wire-form mechanically, closing a workspace-wide
275    /// operator-facing wire-form drift.
276    ///
277    /// Return-tuple axis order matches
278    /// [`crate::owner_reference_json`]'s positional-argument order
279    /// exactly (`fn owner_reference_json(name: &str, uid: &str) ->
280    /// Value`), so the caller composes without a per-callsite axis-
281    /// swap step.
282    ///
283    /// The name gate fires BEFORE the uid gate: on a `Process` fixture
284    /// missing BOTH slots, the returned error names `metadata.name`
285    /// (matches [`Self::owned_coordinates_or_err`]'s two-gate
286    /// ordering, so the two owned-required-extract primitives in the
287    /// family report the same "first-missing-slot" slug at the paired
288    /// missing-both corner).
289    ///
290    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
291    /// the paired `.metadata.<slot>.clone().ok_or_else(...)` chain
292    /// restated two hand-authored gates past the ★★ PRIME-DIRECTIVE
293    /// ≥ 2 duplication trigger). THEORY.md §II.1 invariant 5
294    /// (composition preserves proofs — a regression that swapped the
295    /// two gates, drifted the wire-form back to the lowercase-verb
296    /// spelling, reshaped the axis order of the return tuple, or
297    /// relaxed either gate to a silent-string fallback surfaces at
298    /// the tests below rather than as silent operator-facing drift at
299    /// every downstream [`crate::owner_reference_json`] consumer that
300    /// ingests this pair positionally).
301    pub fn owned_name_and_uid_or_err(&self) -> anyhow::Result<(String, String)> {
302        let name = self
303            .metadata
304            .name
305            .clone()
306            .ok_or_else(|| anyhow::anyhow!("Process has no metadata.name"))?;
307        let uid = self
308            .metadata
309            .uid
310            .clone()
311            .ok_or_else(|| anyhow::anyhow!("Process has no metadata.uid"))?;
312        Ok((name, uid))
313    }
314
315    /// `(namespace, name)` coordinates in the BORROW + NAME-REQUIRED
316    /// corner of the primitive family — namespace half falls back to
317    /// [`Self::DEFAULT_NAMESPACE`], but the name half is REQUIRED
318    /// (`None` on a `Process` whose `metadata.name` is absent, so the
319    /// caller stops with an `else { continue; }` / `else { return
320    /// …; }` guard rather than proceeding with the empty-string
321    /// sentinel every pre-lift consumer had to spell inline).
322    ///
323    /// Peer to [`Self::coordinates_or_defaults`] +
324    /// [`Self::owned_coordinates_or_err`] on the (return-form ×
325    /// name-gate) axis pair — closes the corner the family previously
326    /// left open:
327    ///
328    /// * borrow + name-defaulted → [`Self::coordinates_or_defaults`]
329    ///   (annotation writers, render owner-metadata seed — consumers
330    ///   whose downstream tolerates the `"unnamed"` display placeholder
331    ///   without operator-visible failure);
332    /// * borrow + name-required → **this method** (claim-arbiter
333    ///   probes, child-Process delete-fan-out — consumers that need a
334    ///   real API-path leaf and cleanly SKIP the row when the name is
335    ///   absent rather than issuing a K8s call with an empty-string
336    ///   name argument);
337    /// * owned + name-required → [`Self::owned_coordinates_or_err`]
338    ///   (kube-rs API-path calls — consumers whose downstream requires
339    ///   owned `String` arguments and rejects the missing-name corner
340    ///   with a load-bearing error message).
341    ///
342    /// The primitive family's `None`-on-missing-name semantics
343    /// intentionally differs from [`Self::owned_coordinates_or_err`]'s
344    /// error-on-missing-name semantics: the caller sites for this form
345    /// (child-Process fan-out, claim-arbiter row probes) are non-fatal
346    /// SKIPS rather than reportable failures — an `Option::None` at
347    /// the primitive lets the caller thread that "skip" through a
348    /// let-else without stringifying / logging an anyhow chain per
349    /// missing-name occurrence.
350    ///
351    /// The namespace fallback matches [`Self::coordinates_or_defaults`]
352    /// (via [`Self::namespace_or_default`]), so a consumer that
353    /// switches between the two borrow-form primitives based on its
354    /// name-gate need never sees a different namespace-fallback string
355    /// as a side effect.
356    pub fn coordinates_or_none(&self) -> Option<(&str, &str)> {
357        let name = self.metadata.name.as_deref()?;
358        Some((self.namespace_or_default(), name))
359    }
360
361    /// Canonical `<ns>/<name>` **namespace-qualified process reference**
362    /// composed straight off the live [`Process`] — the ONE-liner
363    /// collapse of the paired
364    /// `let (ns, name) = process.coordinates_or_defaults(); let r =
365    /// qualified_process_ref(ns, name);` incantation every consumer
366    /// whose downstream keys a Process by "which cluster location owns
367    /// it" hand-authored at scattered sites across `tatara-reconciler`.
368    ///
369    /// Pre-lift the 2-step `coordinates_or_defaults() →
370    /// qualified_process_ref(ns, name)` composition was hand-authored
371    /// at THREE sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
372    /// threshold in `tatara-reconciler`, each restating the SAME
373    /// paired projection + `<ns>/<name>` shape:
374    /// * `render::render_routing` — routing-graph `PROCESS=<ref>`
375    ///   annotation seed on every emitted Ingress / DNSEndpoint,
376    ///   feeding [`crate::status::FluxResourceRef`] downstream.
377    /// * `render::render_export_jobs` — export-Job `PROCESS=<ref>`
378    ///   annotation seed on every emitted export `batch/v1` Job.
379    /// * `table_controller::reconcile` — claim-arbiter row-key +
380    ///   `Candidate.process_ref` seed on the stable-name claim
381    ///   registry (the reference lands verbatim in
382    ///   [`crate::table::ClaimRecord.holder`], where every downstream
383    ///   claim query greps it).
384    ///
385    /// All THREE sites walked the SAME 2-step chain — pull the
386    /// `(ns, name)` pair through [`Self::coordinates_or_defaults`],
387    /// then feed the pair positionally into
388    /// [`crate::qualified_process_ref`]. Post-lift each caller reads
389    /// `process.qualified_ref()` — the paired projection + shape
390    /// composer now sit at ONE substrate owner, so a rename of either
391    /// workspace-wide fallback (`"default"` / `"unnamed"`), a swap of
392    /// the `<ns>/<name>` separator, a normalization pass inserted
393    /// between the paired projection and the shape composer, or a
394    /// future `<ns>/<name>@<gen>` / `<cluster>/<ns>/<name>` cross-
395    /// cluster extension lands here exactly once and every consumer
396    /// (annotation seed, claim-row key, holder-slot writer, export-
397    /// Job seed, `Candidate` composer) inherits the upgrade
398    /// mechanically.
399    ///
400    /// Peer to [`Self::coordinates_or_defaults`] on the (return-form ×
401    /// composition-depth) axis pair:
402    /// * pair + defaulted → [`Self::coordinates_or_defaults`]
403    ///   (consumers that thread each half into a separate positional
404    ///   slot — `Api::namespaced(client, &ns) + Api::patch(&name, …)`,
405    ///   `one_export_job(ns, name, …)`, `EdgeContext { process_name,
406    ///   process_namespace, … }`);
407    /// * shape + defaulted → **this method** (consumers that key on
408    ///   the composed `<ns>/<name>` reference directly — the
409    ///   `PROCESS=<ref>` annotation seed, the `ClaimRecord.holder`
410    ///   slot, the label-selector composer).
411    ///
412    /// The namespace-fallback discipline matches
413    /// [`Self::coordinates_or_defaults`] (via
414    /// [`Self::namespace_or_default`]) and the name-fallback discipline
415    /// matches [`Self::name_or_placeholder`], so a consumer that
416    /// switches between the pair-returning primitive and this shape-
417    /// composing primitive never sees a different fallback string as
418    /// a side effect. The composed reference is byte-identical to the
419    /// pre-lift hand-authored `format!("{ns}/{name}")` with `ns` /
420    /// `name` supplied by the pair-returning primitive, so downstream
421    /// greps keyed on the reference shape (`PROCESS=<ref>` on emitted
422    /// resources, `holder = <ref>` on claim-registry queries) match
423    /// bytewise post-lift.
424    ///
425    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
426    /// the 2-step paired-projection + shape-composer chain recurred at
427    /// three hand-authored sites past the ★★ PRIME-DIRECTIVE ≥ 2
428    /// duplication trigger, and is lifted onto ONE workspace-wide
429    /// owner here). THEORY.md §II.1 invariant 5 (composition preserves
430    /// proofs — a regression that inserted a normalization step at
431    /// only two of three sites, or that drifted the fallback strings
432    /// between the paired projection and the shape composer, surfaces
433    /// at [`tests::qualified_ref_*`] rather than as silent operator-
434    /// visible skew across the three annotation / claim-key /
435    /// export-Job seed writers).
436    #[must_use]
437    pub fn qualified_ref(&self) -> String {
438        let (ns, name) = self.coordinates_or_defaults();
439        crate::qualified_process_ref(ns, name)
440    }
441
442    /// Borrowed lookup of ONE key in `metadata.annotations`, with
443    /// BOTH the missing-`annotations` corner AND the missing-key
444    /// corner collapsed to `None` — the ONE-liner collapse of the
445    /// paired `self.metadata.annotations.as_ref().and_then(|m|
446    /// m.get(key)).map(String::as_str)` incantation every consumer
447    /// restated by hand pre-lift.
448    ///
449    /// Pre-lift the 3-line `.metadata.annotations.as_ref().and_then
450    /// (|m| m.get(KEY))` chain (in three tail variants — `.cloned()`,
451    /// `.cloned().unwrap_or_default()`, `.map(String::as_str)`) was
452    /// hand-authored at THREE sites past the ★★ PRIME-DIRECTIVE ≥ 2
453    /// duplication threshold across the workspace:
454    /// * `tatara-reconciler::signals::ingest` — SIGNAL annotation
455    ///   lookup (pre-lift `.cloned()` for owned parsing).
456    /// * `tatara-reconciler::phase_machine::released_from_annotation`
457    ///   — RELEASED_FROM annotation lookup (pre-lift `.cloned()
458    ///   .unwrap_or_default()` for `match v.as_str()`).
459    /// * `tatara-pool-reconciler::controller_pool::process_belongs_to_pool`
460    ///   — POOL annotation lookup (pre-lift `.map(String::as_str)`
461    ///   for `== Some(pool_name)`).
462    ///
463    /// All THREE sites walked the SAME 3-line chain — read the
464    /// annotations map, gate on presence, index by key — differing
465    /// only in the tail that shaped the result. Post-lift each
466    /// caller routes through the ONE substrate primitive here and
467    /// applies its own tail at its own site (`.map(str::to_string)`
468    /// / bare match / `==`).
469    ///
470    /// Return-form axis: `Option<&str>` mirrors the existing borrow-
471    /// first discipline of the peer metadata primitives
472    /// [`Self::namespace_or_default`], [`Self::name_or_placeholder`],
473    /// [`Self::coordinates_or_none`]. The two corners the chain
474    /// swallowed pre-lift (missing `metadata.annotations` map,
475    /// missing key inside the map) BOTH collapse to `None` so
476    /// `.is_some()` / `if let Some(_)` / `Option::map` behave
477    /// identically on a `Process` whose annotations block is `None`
478    /// and on one whose annotations block is populated but omits the
479    /// key — matching what the pre-lift `.and_then(...)` chain
480    /// produced.
481    ///
482    /// A future normalization step (a key-canonicalization pass,
483    /// a case-fold lookup, a per-key alias table for renamed
484    /// annotations across API versions, a per-namespace override
485    /// substrate) lands at ONE substrate method here and all three
486    /// downstream consumers pick up the upgrade mechanically — no
487    /// per-callsite hand-edit at `ingest` / `released_from_annotation`
488    /// / `process_belongs_to_pool`.
489    ///
490    /// Sibling to the peer metadata primitives
491    /// ([`Self::namespace_or_default`], [`Self::name_or_placeholder`],
492    /// [`Self::coordinates_or_defaults`], [`Self::coordinates_or_none`],
493    /// [`Self::owned_coordinates_or_err`]) on the metadata axis;
494    /// this method opens the borrow-form peer on the ANNOTATION
495    /// axis. Future annotation projections (a paired
496    /// `label(&str) -> Option<&str>` on `metadata.labels`, a
497    /// `has_annotation(&str) -> bool` boolean gate for presence-
498    /// only consumers) land as peer methods on this same axis.
499    ///
500    /// Theory anchor: THEORY.md §VI.1 (generation over composition
501    /// — the 3-line annotation-lookup chain recurred at three
502    /// hand-authored sites past the ★★ PRIME-DIRECTIVE ≥ 2
503    /// duplication trigger, and is lifted to ONE owner here).
504    /// THEORY.md §II.1 invariant 5 (composition preserves proofs —
505    /// the pins bind the missing-`annotations` corner + the
506    /// missing-key corner + the borrow-form `&str` lifetime + the
507    /// byte-identical parity with the pre-lift 3-line chain, so a
508    /// regression that drifted any surface at
509    /// `tests::annotation_*` rather than as silent operator-facing
510    /// skew between the SIGNAL / RELEASED_FROM / POOL annotation
511    /// readers).
512    pub fn annotation(&self, key: &str) -> Option<&str> {
513        self.metadata
514            .annotations
515            .as_ref()
516            .and_then(|m| m.get(key))
517            .map(String::as_str)
518    }
519
520    /// Borrow-form metadata-projection primitive on the `metadata.uid`
521    /// axis: returns the K8s-API-server-assigned uid as a `&str`, with
522    /// the missing-uid corner collapsed to the load-bearing empty-string
523    /// sentinel — the ONE-liner collapse of the paired
524    /// `self.metadata.uid.as_deref().unwrap_or("")` incantation every
525    /// owner-reference-emitting consumer restated by hand pre-lift.
526    ///
527    /// The empty-string fallback is NOT arbitrary — it is the exact
528    /// sentinel value the sibling substrate composer
529    /// [`crate::owner_references_json`] gates on (`if uid.is_empty()
530    /// { vec![] } else { vec![owner_reference_json(name, uid)] }`) to
531    /// stamp `metadata.ownerReferences: []` on a resource whose owning
532    /// Process pre-dates the API server's `metadata.uid` assignment
533    /// (test fixture, mid-Forking snapshot before the first `patch`
534    /// round-trip, dynamic API response pre-uid-resolution). Pre-lift
535    /// each consumer spelled the fallback as `.unwrap_or("")` at its
536    /// callsite; the two literals in two files could drift silently to
537    /// `.unwrap_or_default()`, `.unwrap_or("<unknown>")`, or an
538    /// `if let Some(u) = &process.metadata.uid` gate that returned a
539    /// different owner-refs shape for the missing-uid corner. Post-lift
540    /// the sentinel value is composed at ONE substrate site so the
541    /// empty-uid gate at `owner_references_json` and its per-callsite
542    /// producers share the SAME `""` byte-string, and a rename of the
543    /// sentinel would land at ONE substrate site rather than at every
544    /// downstream `owner_references_json(name, uid)` call.
545    ///
546    /// Peer to [`Self::namespace_or_default`] +
547    /// [`Self::name_or_placeholder`] on the metadata-slot × fallback-
548    /// shape axis: `namespace_or_default` returns the K8s-canonical
549    /// `"default"` fallback (matching what the API server substitutes
550    /// on namespaced writes with no explicit namespace);
551    /// `name_or_placeholder` returns the workspace-wide `"unnamed"`
552    /// sentinel (a display placeholder for downstream grepping /
553    /// label-selecting); this method returns the empty-string sentinel
554    /// (a load-bearing gate value that composes with
555    /// [`crate::owner_references_json`]'s `is_empty` check). The three
556    /// primitives partition the metadata-slot family by whether the
557    /// consumer wants a K8s-canonical fallback (namespace), a display
558    /// placeholder (name), or a gate sentinel (uid).
559    ///
560    /// Pre-lift the `.metadata.uid.as_deref().unwrap_or("")` chain was
561    /// hand-authored at TWO sites past the ★★ PRIME-DIRECTIVE ≥ 2
562    /// duplication threshold in `tatara-reconciler::render`, both
563    /// feeding a downstream owner-reference emitter:
564    /// * `render_routing` — the routing-edge seed that binds
565    ///   `process_uid` into every routing-form `EdgeContext` (Ingress +
566    ///   DNSEndpoint) built inside the fanout loop over
567    ///   `RoutingSpec::hostnames`; each `Edge::render` impl then walks
568    ///   its `EdgeContext` through `build_owner_refs` →
569    ///   [`crate::owner_references_json`] to stamp
570    ///   `metadata.ownerReferences` on the emitted resource.
571    /// * `render_export_jobs` — the ephemeral-export Job builder that
572    ///   passes the same uid slice to `tatara_process::
573    ///   owner_references_json(name, uid)` per rendered Job, stamping
574    ///   the export-Job's `metadata.ownerReferences` back at the
575    ///   owning Process.
576    ///
577    /// Both sites walked the SAME `.as_deref().unwrap_or("")` chain and
578    /// both wanted the `&str` form the primitive returns — as the
579    /// second positional argument to `owner_references_json(name, uid)`
580    /// on the ownership-tag axis. Post-lift each callsite reads
581    /// `let uid = process.uid_or_empty();` and the produced slice feeds
582    /// the same downstream composer unchanged.
583    ///
584    /// Return-form axis: `&str` mirrors the existing borrow-first
585    /// discipline of the peer metadata-fallback primitives
586    /// ([`Self::namespace_or_default`], [`Self::name_or_placeholder`]);
587    /// all three return owned-metadata borrows with a slot-specific
588    /// fallback baked in so downstream consumers compose the slice
589    /// directly into their next call without re-spelling the fallback.
590    ///
591    /// A future normalization step (a canonicalization pass that
592    /// rejects a malformed uid before the owner-ref stamp, a cross-
593    /// cluster uid rewrite for multi-tenant control planes, a stale-
594    /// uid warning annotation for a Process whose uid changed under
595    /// the reconciler mid-generation) lands at ONE substrate method
596    /// here and both downstream `owner_references_json` consumers
597    /// pick up the upgrade mechanically — no per-callsite hand-edit
598    /// at `render_routing` / `render_export_jobs`.
599    ///
600    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
601    /// the `.metadata.uid.as_deref().unwrap_or("")` chain recurred at
602    /// two hand-authored sites past the ★★ PRIME-DIRECTIVE ≥ 2
603    /// duplication trigger, and is lifted to ONE owner here).
604    /// THEORY.md §II.1 invariant 5 (composition preserves proofs —
605    /// the pins bind the missing-uid corner + the empty-string
606    /// sentinel byte-shape + the borrow-form `&str` lifetime + the
607    /// byte-identical parity with the pre-lift chain + the composition
608    /// coherence with [`crate::owner_references_json`]'s `is_empty`
609    /// gate, so a regression that drifted any surface at
610    /// `tests::uid_or_empty_*` rather than as silent operator-facing
611    /// skew between the two owner-reference emitters on the SAME
612    /// Process).
613    pub fn uid_or_empty(&self) -> &str {
614        self.metadata.uid.as_deref().unwrap_or("")
615    }
616
617    /// Owned-form metadata-projection primitive on the `metadata.name`
618    /// axis: returns an owned `String` copy of the K8s object name, with
619    /// the missing-name corner collapsed to the load-bearing empty-string
620    /// sentinel — the ONE-liner collapse of the paired
621    /// `self.metadata.name.clone().unwrap_or_default()` incantation every
622    /// keying / row-builder consumer restated by hand pre-lift.
623    ///
624    /// Pre-lift the `.metadata.name.clone().unwrap_or_default()` chain
625    /// was hand-authored at TWO sites past the ★★ PRIME-DIRECTIVE ≥ 2
626    /// duplication threshold in `tatara-pool-reconciler::controller_pool`,
627    /// both stamping the `PoolMember` / `PoolMemberSnapshot`
628    /// `process_name: String` slot inside a struct-literal fanout over
629    /// pool-owned `Process`es:
630    /// * `reconcile_pool`'s pool-member seed (annotation-matched Process
631    ///   list → `PoolMember { process_name, state, entered_state_at, .. }`)
632    ///   — the row every operator sees on the pool's status page.
633    /// * `reconcile_pool`'s desired-count snapshot seed
634    ///   (`PoolMemberSnapshot { process_name, phase, created_at }`)
635    ///   — the row fed into `decide_pool_convergence`.
636    ///
637    /// Both sites walked the SAME `.clone().unwrap_or_default()` chain
638    /// and both wanted the `String` form the primitive returns — as the
639    /// owned-form `process_name: String` slot on a struct literal
640    /// composed inside a `.iter().map(...)` fanout over the same
641    /// pool-owned `Process` list. Post-lift each callsite reads
642    /// `process_name: p.owned_name_or_empty()` and the produced value
643    /// feeds the same struct-literal slot unchanged.
644    ///
645    /// The empty-string fallback is the SAME sentinel the sibling
646    /// borrow-form primitive [`Self::uid_or_empty`] returns — the two
647    /// primitives partition the owned-form × borrow-form corner of the
648    /// metadata-slot family on identical fallback semantics (empty
649    /// string means "the slot is unset"), so a consumer that switches
650    /// between them based on downstream ownership requirements never
651    /// sees a different missing-slot spelling as a side effect.
652    ///
653    /// Peer to [`Self::name_or_placeholder`] on the (return-form ×
654    /// fallback-value) axis pair — closes the corner the family
655    /// previously left open:
656    ///
657    /// * borrow + display placeholder → [`Self::name_or_placeholder`]
658    ///   (log lines, annotation writers, ownership-tag composers —
659    ///   consumers whose downstream drops `"unnamed"` in place of a
660    ///   missing name without operator-visible failure);
661    /// * owned + empty sentinel → **this method** (row-builder /
662    ///   HashMap-key / struct-literal fanout consumers whose downstream
663    ///   fills a `String` field with the load-bearing `""` sentinel to
664    ///   flag "no name to key by" rather than substituting a display
665    ///   placeholder that would misalign a downstream lookup);
666    /// * owned + name-required → [`Self::owned_coordinates_or_err`] (kube-rs
667    ///   API-path calls — consumers whose downstream must NOT silently
668    ///   substitute a placeholder for the API call target).
669    ///
670    /// The primitive family's `""`-on-missing-name semantics
671    /// intentionally differs from [`Self::name_or_placeholder`]'s
672    /// `"unnamed"` semantics: the caller sites for this form (pool
673    /// membership row seeds, HashMap keys) are load-bearing keys — a
674    /// display placeholder like `"unnamed"` would silently alias every
675    /// missing-name Process to the same key, collapsing distinct rows
676    /// in the pool's member list. The empty-string sentinel keeps the
677    /// pre-lift byte-shape and lets downstream consumers gate on
678    /// `String::is_empty` if they need to filter the missing-name
679    /// corner explicitly.
680    ///
681    /// A future normalization step (a name-canonicalization pass, a
682    /// case-fold key builder, a per-pool alias table for renamed
683    /// Processes across generations) lands at ONE substrate method
684    /// here and both downstream `PoolMember` / `PoolMemberSnapshot`
685    /// seeds pick up the upgrade mechanically — no per-callsite hand-
686    /// edit at `reconcile_pool`.
687    ///
688    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
689    /// the `.metadata.name.clone().unwrap_or_default()` chain recurred
690    /// at two hand-authored sites past the ★★ PRIME-DIRECTIVE ≥ 2
691    /// duplication trigger, and is lifted to ONE owner here).
692    /// THEORY.md §II.1 invariant 5 (composition preserves proofs —
693    /// the pins bind the missing-name corner + the empty-string
694    /// sentinel byte-shape + the owned-form `String` return type +
695    /// the byte-identical parity with the pre-lift chain + the
696    /// fallback-value coherence with the sibling [`Self::uid_or_empty`]
697    /// on the metadata-slot × empty-sentinel axis, so a regression
698    /// that drifted any surface at `tests::owned_name_or_empty_*`
699    /// rather than as silent operator-facing skew between the pool-
700    /// member seed and the desired-count snapshot seed on the SAME
701    /// pool).
702    pub fn owned_name_or_empty(&self) -> String {
703        self.metadata.name.clone().unwrap_or_default()
704    }
705
706    /// Borrow-form spec-projection primitive on the declared parent-PID
707    /// axis: returns the hierarchical PID path (e.g. `"seph.1"`) the
708    /// author declared at `spec.identity.parent`, with the empty-slot
709    /// corner collapsed to `None` — the ONE-liner collapse of the
710    /// paired `self.spec.identity.parent.as_deref()` incantation every
711    /// consumer restated by hand pre-lift.
712    ///
713    /// Pre-lift the `.spec.identity.parent.as_deref()` chain was hand-
714    /// authored at TWO sites past the ★★ PRIME-DIRECTIVE ≥ 2
715    /// duplication threshold in `tatara-reconciler::phase_machine`:
716    /// * `handle_forking` — the ALLOCATE-PID composer that threads the
717    ///   declared parent PID into [`pid::allocate_pid`] and also into
718    ///   the status patch payload (`{ "pid": new_pid, "parent":
719    ///   parent_pid }`), so the reconciler-observed
720    ///   [`ProcessStatus::parent`] slot mirrors the author-declared
721    ///   [`IdentitySpec::parent`] at fork time. The `info!` tracing
722    ///   span also reads the same slice as the `parent` field on the
723    ///   PID-assigned log line.
724    /// * `handle_exiting` — the SIGTERM cascade's child-fan-out filter
725    ///   that enumerates every Process cluster-wide and picks children
726    ///   whose `spec.identity.parent` equals this Process's currently-
727    ///   observed PID (`.filter(|c| c.spec.identity.parent.as_deref()
728    ///   == Some(pid))`). The filter runs per candidate child, so the
729    ///   borrow-form projection avoids allocating one `String` clone
730    ///   per non-matching row in the cluster-wide list.
731    ///
732    /// Both sites walked the SAME `.as_deref()` chain and both wanted
733    /// the `Option<&str>` form the primitive returns — the
734    /// `handle_forking` site to feed positionally into
735    /// `pid::allocate_pid(&identity, parent_pid, next_seq)` and the
736    /// tracing span's `parent = ?parent_pid` debug print + the JSON
737    /// payload's `"parent": parent_pid` slot; the `handle_exiting`
738    /// filter to compare directly against `Some(pid)` where `pid:
739    /// &str` came off the borrow-form peer [`Self::observed_pid`].
740    ///
741    /// Return-form axis: `Option<&str>` mirrors the borrow-first
742    /// discipline of every peer primitive on the metadata / status
743    /// slot family ([`Self::namespace_or_default`],
744    /// [`Self::name_or_placeholder`], [`Self::observed_pid`],
745    /// [`Self::annotation`]). The empty-slot corner
746    /// (`spec.identity.parent = None`, matching `init` / PID 1 with
747    /// no parent) collapses to `None` so `.is_some()` / `if let
748    /// Some(_)` / `.map(...)` behave identically on a `Process`
749    /// authored at cluster init (PID 1, parent absent) and on any
750    /// PID-N child (parent present) — matching the pre-lift
751    /// `.as_deref()` chain's `None` byte-identically.
752    ///
753    /// Peer to [`Self::observed_pid`] on the (spec-declared ×
754    /// status-observed) axis pair: `observed_pid` returns the PID
755    /// path this Process currently OWNS (the reconciler-persisted
756    /// child position in the hierarchy), while `declared_parent_pid`
757    /// returns the PID path this Process's parent OWNS (the author-
758    /// declared upstream position). The SIGTERM cascade at
759    /// `handle_exiting` composes both: it reads its own
760    /// [`Self::observed_pid`] and matches each candidate child's
761    /// [`Self::declared_parent_pid`] against that value — the child-
762    /// fan-out relation IS the spec-declared × status-observed axis
763    /// pair collapsed to a single comparator, both sides routed
764    /// through the same borrow-form skeleton.
765    ///
766    /// A future normalization step (a per-slot canonicalization pass
767    /// that rejects malformed hierarchical PIDs, a case-fold lookup
768    /// against a table of renamed identities, a cross-cluster prefix
769    /// stripper, an alias-table lookup that maps a legacy PID to its
770    /// current spelling) lands at ONE substrate method here and both
771    /// downstream consumers pick up the upgrade mechanically — no
772    /// per-callsite hand-edit at `handle_forking` / `handle_exiting`.
773    ///
774    /// Sibling to the peer metadata-projection primitives
775    /// ([`Self::namespace_or_default`], [`Self::name_or_placeholder`],
776    /// [`Self::coordinates_or_defaults`], [`Self::coordinates_or_none`],
777    /// [`Self::owned_coordinates_or_err`], [`Self::annotation`]) on the
778    /// metadata axis; this method opens the borrow-form peer on the
779    /// declared-identity axis. Future identity projections
780    /// (`declared_name_override` on the `spec.identity.name_override`
781    /// axis, a paired `declared_identity` composite that returns both
782    /// halves) land as peer methods on this same axis.
783    ///
784    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
785    /// the `.spec.identity.parent.as_deref()` chain recurred at two
786    /// hand-authored sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
787    /// trigger, and is lifted to ONE owner here). THEORY.md §II.1
788    /// invariant 5 (composition preserves proofs — the pins bind the
789    /// empty-slot corner + the borrow-form `&str` lifetime + the
790    /// byte-identical parity with the pre-lift `.as_deref()` chain,
791    /// so a regression that drifted any surface at
792    /// `tests::declared_parent_pid_*` rather than as silent operator-
793    /// facing skew between the ALLOCATE-PID composer and the SIGTERM
794    /// cascade's child-fan-out filter on the SAME parent-child pair).
795    pub fn declared_parent_pid(&self) -> Option<&str> {
796        self.spec.identity.parent.as_deref()
797    }
798
799    /// Borrow-form spec-projection primitive on the declared
800    /// name-override axis: returns the human name the author declared
801    /// at `spec.identity.name_override` (used verbatim instead of the
802    /// content-hash-derived name in [`derive_identity`]), with the
803    /// empty-slot corner collapsed to `None` — the ONE-liner collapse
804    /// of the paired `self.spec.identity.name_override.as_deref()`
805    /// incantation every consumer restated by hand pre-lift.
806    ///
807    /// Pre-lift the `.spec.identity.name_override.as_deref()` chain
808    /// was hand-authored at TWO sites past the ★★ PRIME-DIRECTIVE ≥ 2
809    /// duplication threshold in `tatara-reconciler::phase_machine`,
810    /// both feeding the second positional argument of
811    /// [`derive_identity`]:
812    /// * `handle_pending` — the DECLARE composer that computes the
813    ///   Process's [`Identity`] on entry to the state machine (before
814    ///   `patch::phase_status` writes it into `status.identity`).
815    /// * `handle_forking` — the ALLOCATE-PID composer that recomputes
816    ///   the same [`Identity`] on a rehydration path (status may
817    ///   already carry an identity from a prior reconcile, in which
818    ///   case the `.and_then(|s| s.identity.clone())` short-circuit
819    ///   takes it; otherwise this `.unwrap_or_else` branch fires and
820    ///   recomputes the identity fresh from the spec) so `pid::
821    ///   allocate_pid` sees the SAME [`Identity`] the DECLARE phase
822    ///   produced.
823    ///
824    /// Both sites walked the SAME `.as_deref()` chain and both wanted
825    /// the `Option<&str>` form the primitive returns — as the second
826    /// positional argument to `derive_identity(&self.spec, …)`, which
827    /// internally trims + filters empty strings + dispatches on
828    /// `Some(non_empty)` (verbatim name, `name_override: true`) vs
829    /// `None | Some(empty | whitespace)` (content-hash-derived name,
830    /// `name_override: false`). The primitive itself preserves the
831    /// raw slot byte-identically (the trim happens IN
832    /// `derive_identity`, not at the borrow site), so the two live
833    /// paths compose through the SAME borrow-form skeleton.
834    ///
835    /// Return-form axis: `Option<&str>` mirrors the borrow-first
836    /// discipline of every peer primitive on the metadata / status /
837    /// spec-identity slot family ([`Self::namespace_or_default`],
838    /// [`Self::name_or_placeholder`], [`Self::observed_pid`],
839    /// [`Self::annotation`], [`Self::declared_parent_pid`]). The
840    /// empty-slot corner (`spec.identity.name_override = None`,
841    /// matching a Process authored WITHOUT the human-name-override
842    /// escape hatch — the default; `derive_identity` then computes
843    /// the name from the content hash) collapses to `None` so
844    /// `.is_some()` / `if let Some(_)` / `.map(...)` behave
845    /// identically on the two Process shapes an operator can author.
846    ///
847    /// Peer to [`Self::declared_parent_pid`] on the (parent × name-
848    /// override) sub-axis of the declared-identity axis: both
849    /// primitives project a `Option<String>` slot on `IdentitySpec`
850    /// through the SAME borrow-form skeleton, so a future
851    /// `declared_identity` composite that returns both halves
852    /// together (e.g. as a `(Option<&str>, Option<&str>)` tuple or a
853    /// borrow-form `DeclaredIdentityView<'_>` newtype) lands as ONE
854    /// method that COMPOSES the two peer primitives, not as three
855    /// hand-authored `.as_deref()` chains restated at each callsite.
856    ///
857    /// A future normalization step (a per-slot canonicalization pass
858    /// that rejects malformed names, a case-fold lookup against a
859    /// table of renamed identities, an alias-table lookup that maps
860    /// a legacy name-override to its current spelling, a whitespace-
861    /// trim lift OUT of `derive_identity` INTO the primitive so both
862    /// consumers see the trimmed form) lands at ONE substrate method
863    /// here and both downstream consumers pick up the upgrade
864    /// mechanically — no per-callsite hand-edit at `handle_pending` /
865    /// `handle_forking`.
866    ///
867    /// Sibling to the peer spec-identity projection
868    /// [`Self::declared_parent_pid`] on the declared-identity axis;
869    /// this method opens the borrow-form peer on the name-override
870    /// sub-axis of the same closed set (`IdentitySpec { parent,
871    /// name_override }`). Future identity projections (a paired
872    /// `declared_identity` composite that returns both halves
873    /// together) land as peer methods on this same axis.
874    ///
875    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
876    /// the `.spec.identity.name_override.as_deref()` chain recurred
877    /// at two hand-authored sites past the ★★ PRIME-DIRECTIVE ≥ 2
878    /// duplication trigger, and is lifted to ONE owner here).
879    /// THEORY.md §II.1 invariant 5 (composition preserves proofs —
880    /// the pins bind the empty-slot corner + the borrow-form `&str`
881    /// lifetime + the byte-identical parity with the pre-lift
882    /// `.as_deref()` chain + the invariance under
883    /// [`derive_identity`]'s internal trim/filter step, so a
884    /// regression that drifted any surface at
885    /// `tests::declared_name_override_*` rather than as silent
886    /// operator-facing skew between the DECLARE composer and the
887    /// ALLOCATE-PID rehydration branch on the SAME Process spec).
888    pub fn declared_name_override(&self) -> Option<&str> {
889        self.spec.identity.name_override.as_deref()
890    }
891
892    /// Borrowed slice of the FluxCD resources this Process's status
893    /// currently persists at `status.flux_resources`, with the
894    /// missing-`status` corner collapsed to an empty slice — the ONE-
895    /// line collapse of the paired `self.status.as_ref().map(|s|
896    /// s.flux_resources.clone()).unwrap_or_default()` incantation
897    /// every VERIFY-phase / ATTEST-heartbeat consumer restated by hand
898    /// pre-lift.
899    ///
900    /// Pre-lift the 5-line `.status.as_ref().map(|s| s.flux_resources
901    /// .clone()).unwrap_or_default()` chain was hand-authored at TWO
902    /// sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold in
903    /// `tatara-reconciler::phase_machine`:
904    /// * `handle_running` — the VERIFY-phase per-ref readiness probe
905    ///   seed that walks every ref through
906    ///   [`crate::status::FluxResourceRef::fetch_coords`] via
907    ///   `ssapply::fetch_flux_ref` and rebuilds an updated
908    ///   `Vec<FluxResourceRef>` with `ready` + `message` + `last_check`
909    ///   observed at reconcile time.
910    /// * `handle_attested` — the ATTEST-heartbeat drift detector that
911    ///   short-circuits on the first non-Ready ref via
912    ///   `ssapply::fetch_flux_ref` + `ssapply::ready_condition`.
913    ///
914    /// Both sites walked the SAME 5-line chain — clone the vector
915    /// eagerly for the length of the reconcile pass, then iterate it
916    /// by reference — even though neither site ever mutates the vector
917    /// nor keeps it alive past the enclosing async fn. Post-lift both
918    /// callers borrow the slice directly from `self.status`; the two
919    /// pre-lift `.clone()` calls disappear because the slice lives for
920    /// the borrow of `&self`, and both call sites' subsequent
921    /// downstream calls (`ssapply::fetch_flux_ref` / the
922    /// `patch::patch_process_status` write) do not touch the borrowed
923    /// `p: &Process`, so the borrow lifetime holds.
924    ///
925    /// Return-form axis: `&[FluxResourceRef]` mirrors the existing
926    /// borrow-first discipline every pre-lift consumer already
927    /// iterated by reference (`for r in &refs`), and the shape of
928    /// [`crate::status::FluxResourceRef::fetch_coords`]'s per-ref
929    /// borrow projection extends mechanically to the slice-level
930    /// projection here. The missing-`status` corner collapses to the
931    /// empty slice `&[]` so `.is_empty()` / `.len()` / iteration all
932    /// behave identically on a `Process` whose status is `None` and
933    /// on one whose status carries an empty `flux_resources` slot —
934    /// matching what the pre-lift `.unwrap_or_default()` produced
935    /// (an empty `Vec`).
936    ///
937    /// A future normalization step (a per-ref canonicalization pass
938    /// that skips duplicated refs, an owner-filter that returns only
939    /// refs stamped with the CURRENT `metadata.generation`, a
940    /// staleness gate that drops refs whose `last_check` predates a
941    /// reconcile deadline) lands at ONE substrate method here and
942    /// both downstream consumers pick up the upgrade mechanically —
943    /// no per-callsite hand-edit at `handle_running` /
944    /// `handle_attested`.
945    ///
946    /// Sibling to the [`Self::coordinates_or_none`] borrow-first
947    /// primitive on the metadata axis; this method opens the
948    /// analogous borrow-first primitive on the status-projection
949    /// axis. Future status projections (`observed_attestation` on
950    /// the attestation-chain axis, `observed_pid` on the PID axis,
951    /// `observed_children` on the child-fan-out axis) land as peer
952    /// methods on this same axis.
953    ///
954    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
955    /// the 5-line status-projection chain recurred at two hand-
956    /// authored sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
957    /// trigger, and is lifted to ONE owner here). THEORY.md §II.1
958    /// invariant 5 (composition preserves proofs — the pins bind the
959    /// missing-`status` corner + the slice-lifetime borrow discipline
960    /// + the byte-identical parity with the pre-lift 5-line chain, so
961    /// a regression that drifted any of the three surfaces at
962    /// `tests::observed_flux_resources_*` rather than as silent
963    /// operator-facing skew between the VERIFY-phase and ATTEST-
964    /// heartbeat consumers).
965    pub fn observed_flux_resources(&self) -> &[FluxResourceRef] {
966        self.status
967            .as_ref()
968            .map(|s| s.flux_resources.as_slice())
969            .unwrap_or(&[])
970    }
971
972    /// The borrow-form status-projection primitive on the PID axis:
973    /// returns the hierarchical PID path (e.g. `"seph.1.7"`) the
974    /// reconciler currently persists at `status.pid`, with BOTH the
975    /// missing-`status` corner AND the empty-slot corner collapsed
976    /// to `None` — the ONE-liner collapse of the paired
977    /// `self.status.as_ref().and_then(|s| s.pid.clone())` incantation
978    /// every consumer restated by hand pre-lift.
979    ///
980    /// Pre-lift the 3-line `.status.as_ref().and_then(|s| s.pid
981    /// .clone())` chain was hand-authored at TWO sites past the ★★
982    /// PRIME-DIRECTIVE ≥ 2 duplication threshold in
983    /// `tatara-reconciler::phase_machine`:
984    /// * `handle_forking` — the ALLOCATE-PID gate that short-
985    ///   circuits the PID allocator when the reconciler already
986    ///   assigned a PID on a prior reconcile pass (pre-lift the
987    ///   chain composed with `.is_some()` and threw the clone away
988    ///   without ever reading the string).
989    /// * `handle_exiting` — the SIGTERM cascade that enumerates
990    ///   child Processes and terminates them by matching each
991    ///   child's `spec.identity.parent` against the PID this Process
992    ///   currently owns (pre-lift the chain bound an owned
993    ///   `Option<String>` and threaded `pid.as_str()` into the
994    ///   downstream `.as_deref() == Some(...)` comparator).
995    ///
996    /// Both sites walked the SAME 3-line chain — clone the `String`
997    /// eagerly, then either drop it (the `handle_forking` gate) or
998    /// re-borrow it through `.as_str()` (the `handle_exiting`
999    /// comparator) — even though neither site ever mutates the PID
1000    /// nor keeps it alive past the enclosing async fn. Post-lift
1001    /// both callers borrow the PID directly from `self.status`; the
1002    /// pre-lift `.clone()` at both sites disappears because the
1003    /// `&str` lives for the borrow of `&self`, and both call sites'
1004    /// subsequent downstream calls (the K8s API list/patch, the
1005    /// child-Process comparator) do not touch the borrowed
1006    /// `p: &Process`, so the borrow lifetime holds.
1007    ///
1008    /// Return-form axis: `Option<&str>` mirrors the existing
1009    /// borrow-first discipline every pre-lift consumer already
1010    /// re-borrowed through `.as_str()` before use, and the shape of
1011    /// [`Self::coordinates_or_none`]'s `Option<(&str, &str)>`
1012    /// projection extends mechanically to the single-slot
1013    /// projection here. The missing-`status` corner AND the
1014    /// populated-status-with-`pid=None` corner BOTH collapse to
1015    /// `None` so `.is_some()` / `if let Some(_)` / `.map(...)`
1016    /// behave identically on a `Process` whose status is `None`
1017    /// and on one whose status carries an unpopulated `pid` slot —
1018    /// matching what the pre-lift `.and_then(...)` chain produced.
1019    ///
1020    /// A future normalization step (a per-slot canonicalization
1021    /// pass that rejects malformed hierarchical PIDs, a
1022    /// generation-filter that returns `None` for a PID stamped
1023    /// with a stale `metadata.generation`, a staleness gate that
1024    /// drops a PID whose observing `phase_since` predates a
1025    /// reconcile deadline) lands at ONE substrate method here and
1026    /// both downstream consumers pick up the upgrade mechanically
1027    /// — no per-callsite hand-edit at `handle_forking` /
1028    /// `handle_exiting`.
1029    ///
1030    /// Sibling to the peer [`Self::observed_flux_resources`]
1031    /// borrow-first primitive on the flux-resources axis; both
1032    /// methods compose the same missing-`status` fallback +
1033    /// borrow-form return-shape skeleton on distinct
1034    /// `ProcessStatus` slots. Future status projections
1035    /// (`observed_parent` on the parent-pointer axis,
1036    /// `observed_message` on the human-readable-status axis,
1037    /// `observed_attestation` on the attestation-chain axis) land
1038    /// as peer methods on this same axis.
1039    ///
1040    /// Theory anchor: THEORY.md §VI.1 (generation over
1041    /// composition — the 3-line status-projection chain recurred
1042    /// at two hand-authored sites past the ★★ PRIME-DIRECTIVE ≥ 2
1043    /// duplication trigger, and is lifted to ONE owner here).
1044    /// THEORY.md §II.1 invariant 5 (composition preserves proofs —
1045    /// the pins bind the missing-`status` corner + the empty-slot
1046    /// corner + the borrow-form `&str` lifetime + the
1047    /// byte-identical parity with the pre-lift 3-line chain, so a
1048    /// regression that drifted any surface at
1049    /// `tests::observed_pid_*` rather than as silent operator-
1050    /// facing skew between the ALLOCATE-PID gate and the SIGTERM
1051    /// cascade on the SAME `Process`).
1052    pub fn observed_pid(&self) -> Option<&str> {
1053        self.status.as_ref().and_then(|s| s.pid.as_deref())
1054    }
1055
1056    /// The borrow-form status-projection primitive on the
1057    /// attestation-chain axis: returns the last
1058    /// [`ProcessAttestation`] the reconciler persisted at
1059    /// `status.attestation`, with the missing-`status` corner AND the
1060    /// empty-slot corner BOTH collapsed to `None` — the ONE-liner
1061    /// collapse of the paired `self.status.as_ref().and_then(|s|
1062    /// s.attestation.as_ref())` incantation every consumer restated
1063    /// by hand pre-lift.
1064    ///
1065    /// Pre-lift the 3-line `.status.as_ref().and_then(|s| s
1066    /// .attestation.as_ref())` chain was hand-authored at TWO sites
1067    /// past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold in
1068    /// `tatara-reconciler`:
1069    /// * `phase_machine::advance_to_attested` — the ATTEST composer
1070    ///   that chains `prior.next(pillars)` when a prior attestation
1071    ///   is persisted and seeds with `ProcessAttestation::initial`
1072    ///   otherwise.
1073    /// * `render::render_export_jobs` — the ephemeral-export Job
1074    ///   builder that pulls the prior `composed_root` off the last
1075    ///   persisted attestation and threads it into every rendered
1076    ///   Job's `previousRoot` env var, so the export receipt chains
1077    ///   into the Process's BLAKE3 attestation tree at the correct
1078    ///   generation boundary.
1079    ///
1080    /// Both sites walked the SAME 3-line chain — the borrow-form
1081    /// `Option<&ProcessAttestation>` shape both consumers wanted
1082    /// already — even though neither site ever mutated the
1083    /// attestation nor kept it alive past the enclosing async fn.
1084    /// Post-lift both callers borrow the attestation directly from
1085    /// `self.status`; the pre-lift 3-line chain shrinks to a single
1086    /// method call at both sites, and both consumers' subsequent
1087    /// downstream calls (`ProcessAttestation::next` for the ATTEST
1088    /// composer, `.composed_root.clone()` for the export Job builder)
1089    /// do not touch the borrowed `p: &Process`, so the borrow
1090    /// lifetime holds.
1091    ///
1092    /// Return-form axis: `Option<&ProcessAttestation>` mirrors the
1093    /// existing borrow-first discipline every pre-lift consumer
1094    /// already re-borrowed through `.as_ref()`, and the shape of the
1095    /// peer [`Self::observed_pid`] projection extends mechanically
1096    /// to the whole-attestation-record projection here. The missing-
1097    /// `status` corner AND the populated-status-with-`attestation
1098    /// =None` corner BOTH collapse to `None` so `.is_some()` / `if
1099    /// let Some(_)` / `.map(...)` behave identically on a `Process`
1100    /// whose status is `None` and on one whose status carries an
1101    /// unpopulated `attestation` slot — matching what the pre-lift
1102    /// `.and_then(...)` chain produced.
1103    ///
1104    /// A future normalization step (a per-slot canonicalization pass
1105    /// that rejects a persisted attestation whose `composed_root`
1106    /// fails `verify`, a generation-filter that returns `None` for
1107    /// an attestation stamped with a stale `metadata.generation`, a
1108    /// staleness gate that drops an attestation whose `attested_at`
1109    /// predates a reconcile deadline) lands at ONE substrate method
1110    /// here and both downstream consumers pick up the upgrade
1111    /// mechanically — no per-callsite hand-edit at
1112    /// `advance_to_attested` / `render_export_jobs`.
1113    ///
1114    /// Sibling to the peer [`Self::observed_pid`] +
1115    /// [`Self::observed_flux_resources`] borrow-first primitives on
1116    /// the PID + flux-resources axes; all three methods compose the
1117    /// same missing-`status` fallback + borrow-form return-shape
1118    /// skeleton on distinct `ProcessStatus` slots. Future status
1119    /// projections (`observed_parent` on the parent-pointer axis,
1120    /// `observed_message` on the human-readable-status axis) land
1121    /// as peer methods on this same axis.
1122    ///
1123    /// Theory anchor: THEORY.md §VI.1 (generation over composition
1124    /// — the 3-line status-projection chain recurred at two hand-
1125    /// authored sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
1126    /// trigger, and is lifted to ONE owner here). THEORY.md §II.1
1127    /// invariant 5 (composition preserves proofs — the pins bind
1128    /// the missing-`status` corner + the empty-slot corner + the
1129    /// borrow-form `&ProcessAttestation` lifetime + the byte-
1130    /// identical parity with the pre-lift 3-line chain, so a
1131    /// regression that drifted any surface at
1132    /// `tests::observed_attestation_*` rather than as silent
1133    /// operator-facing skew between the ATTEST composer and the
1134    /// ephemeral-export receipt chain on the SAME `Process`).
1135    pub fn observed_attestation(&self) -> Option<&ProcessAttestation> {
1136        self.status.as_ref().and_then(|s| s.attestation.as_ref())
1137    }
1138
1139    /// The borrow-form status-projection primitive on the resolved-
1140    /// identity axis: returns the [`Identity`] the reconciler
1141    /// currently persists at `status.identity` (name + content hash +
1142    /// override flag), with the missing-`status` corner AND the
1143    /// empty-slot corner BOTH collapsed to `None` — the ONE-liner
1144    /// collapse of the paired `self.status.as_ref().and_then(|s|
1145    /// s.identity.as_ref())` incantation every consumer restated by
1146    /// hand pre-lift.
1147    ///
1148    /// Pre-lift the paired `.status.as_ref().and_then(|s|
1149    /// s.identity.<clone|as_ref>())` chain was hand-authored at TWO
1150    /// sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold
1151    /// in `tatara-reconciler`:
1152    /// * `phase_machine::handle_forking` — the FORK-time identity
1153    ///   seed that reuses the reconciler-persisted `Identity` if
1154    ///   present and falls back to a fresh `derive_identity(&spec,
1155    ///   name_override)` otherwise. Pre-lift the site cloned the
1156    ///   whole `Identity` off the borrow before threading it through
1157    ///   `.unwrap_or_else(...)` even though the fallback path
1158    ///   allocates its own owned `Identity` — the pre-lift clone
1159    ///   allocated a fresh `Identity` on the happy path just so the
1160    ///   `Option`'s shape matched the fallback's `Identity` return
1161    ///   type.
1162    /// * `ssapply::inject_annotations` — the SSA-time annotation
1163    ///   composer that stamps the content-hash annotation onto every
1164    ///   owned resource. Pre-lift the site nested the identity
1165    ///   borrow-form check inside a manual `if let Some(status) =
1166    ///   &process.status { … }` guard alongside sibling `status.pid`
1167    ///   and `status.attestation` accesses — three siblings the peer
1168    ///   primitives [`Self::observed_pid`] and
1169    ///   [`Self::observed_attestation`] already own, so the outer
1170    ///   status guard was the last hand-authored `.status.as_ref()`
1171    ///   destructure at this composer.
1172    ///
1173    /// Both sites walked the SAME 3-line chain (one via `.clone()`,
1174    /// one via `.as_ref()`) — the borrow-form
1175    /// `Option<&Identity>` shape both consumers wanted already, even
1176    /// though the FORK-time seed then had to `.clone()` off the
1177    /// borrow to compose with the owned-`Identity` fallback. Post-
1178    /// lift the seed calls `.observed_identity().cloned()` at the
1179    /// exact composition point where the owned value is required
1180    /// (the empty-borrow corner clones nothing, since
1181    /// `Option::cloned` on `None` is `None`), and the SSA-time
1182    /// consumer drops the outer status guard entirely — the
1183    /// three-sibling primitive family (pid + identity + attestation)
1184    /// now peers through `observed_pid` +
1185    /// `observed_identity` + `observed_attestation` at ONE call each
1186    /// with no shared status destructure between them.
1187    ///
1188    /// Return-form axis: `Option<&Identity>` mirrors the
1189    /// existing borrow-first discipline every pre-lift consumer
1190    /// already re-borrowed through `.as_ref()` / re-cloned through
1191    /// `.clone()`, and the shape of the peer
1192    /// [`Self::observed_attestation`] projection extends
1193    /// mechanically to the whole-`Identity`-record projection here.
1194    /// The missing-`status` corner AND the populated-status-with-
1195    /// `identity=None` corner BOTH collapse to `None` so
1196    /// `.is_some()` / `if let Some(_)` / `.map(...)` behave
1197    /// identically on a `Process` whose status is `None` and on one
1198    /// whose status carries an unpopulated `identity` slot —
1199    /// matching what the pre-lift `.and_then(...)` chain produced.
1200    ///
1201    /// A future normalization step (a per-slot canonicalization
1202    /// pass that rejects an `Identity` whose `content_hash` fails
1203    /// re-derivation against the current spec, a generation-filter
1204    /// that returns `None` for an identity stamped with a stale
1205    /// `metadata.generation`, a staleness gate that drops an
1206    /// identity whose observing `phase_since` predates a reconcile
1207    /// deadline) lands at ONE substrate method here and both
1208    /// downstream consumers pick up the upgrade mechanically — no
1209    /// per-callsite hand-edit at `handle_forking` /
1210    /// `inject_annotations`.
1211    ///
1212    /// Sibling to the peer [`Self::observed_pid`] +
1213    /// [`Self::observed_attestation`] +
1214    /// [`Self::observed_flux_resources`] borrow-first primitives on
1215    /// the PID + attestation-chain + flux-resources axes; all four
1216    /// methods compose the same missing-`status` fallback +
1217    /// borrow-form return-shape skeleton on distinct `ProcessStatus`
1218    /// slots. Future status projections (`observed_parent` on the
1219    /// parent-pointer axis, `observed_message` on the human-
1220    /// readable-status axis) land as peer methods on this same
1221    /// axis.
1222    ///
1223    /// Theory anchor: THEORY.md §VI.1 (generation over composition
1224    /// — the 3-line status-projection chain recurred at two hand-
1225    /// authored sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication
1226    /// trigger, and is lifted to ONE owner here). THEORY.md §II.1
1227    /// invariant 5 (composition preserves proofs — the pins bind
1228    /// the missing-`status` corner + the empty-slot corner + the
1229    /// borrow-form `&Identity` lifetime + the byte-identical parity
1230    /// with the pre-lift 3-line chain, so a regression that drifted
1231    /// any surface at `tests::observed_identity_*` rather than as
1232    /// silent operator-facing skew between the FORK-time identity
1233    /// seed and the SSA-time content-hash annotation stamp on the
1234    /// SAME `Process`).
1235    pub fn observed_identity(&self) -> Option<&Identity> {
1236        self.status.as_ref().and_then(|s| s.identity.as_ref())
1237    }
1238
1239    /// The copy-form status-projection primitive on the phase axis:
1240    /// returns the [`ProcessPhase`] the reconciler currently persists
1241    /// at `status.phase`, wrapped in an `Option` so the missing-
1242    /// `status` corner collapses to `None` — the ONE-liner collapse
1243    /// of the paired `self.status.as_ref().map(|s| s.phase)`
1244    /// incantation every consumer restated by hand pre-lift.
1245    ///
1246    /// Peer to the borrow-form projections
1247    /// [`Self::observed_pid`] (PID axis, `Option<&str>`),
1248    /// [`Self::observed_flux_resources`] (flux-resources axis,
1249    /// `&[FluxResourceRef]`), and [`Self::observed_attestation`]
1250    /// (attestation-chain axis, `Option<&ProcessAttestation>`); this
1251    /// method opens the copy-form peer for `ProcessPhase` — a
1252    /// `Copy` scalar with a `Default` impl (`Pending`), so the
1253    /// return is `Option<ProcessPhase>` rather than
1254    /// `Option<&ProcessPhase>` (borrow would give the caller
1255    /// nothing over the copy for a 1-byte enum) and neither the
1256    /// missing-`status` corner nor a "empty slot" corner is
1257    /// meaningful — the underlying slot is a bare `ProcessPhase`,
1258    /// not `Option<ProcessPhase>`, so the primitive returns `None`
1259    /// iff `status: None`.
1260    ///
1261    /// Pre-lift the 3-line `.status.as_ref().map(|s| s.phase)`
1262    /// chain was hand-authored at FIVE sites past the ★★
1263    /// PRIME-DIRECTIVE ≥ 2 duplication threshold in
1264    /// `tatara-reconciler`:
1265    /// * `controller::reconcile` — the top-level dispatcher's
1266    ///   `current_phase` seed that feeds the deletion-preempt +
1267    ///   signal-ingestion gates + the per-phase handler dispatch.
1268    ///   Pre-lift `.unwrap_or(ProcessPhase::Pending)`.
1269    /// * `boundary::evaluate_process_phase` — the boundary
1270    ///   evaluator's `ProcessPhase` condition (a peer-Process
1271    ///   `phase`-reached postcondition). Pre-lift
1272    ///   `.unwrap_or(ProcessPhase::Pending)`.
1273    /// * `boundary::check_depends_on` — the `depends_on`
1274    ///   pre-condition audit that stashes the observed phase into
1275    ///   the `UnmetDependency::actual: Option<ProcessPhase>` slot
1276    ///   (keeps the `Option` form). Pre-lift the raw
1277    ///   `.map(|s| s.phase)` shape.
1278    /// * `phase_machine::p_current_phase_str` — the released-from
1279    ///   annotation composer that emits `"Attested"` for every
1280    ///   non-`Failed` phase (SIGSTOP/SIGCONT release gate).
1281    ///   Pre-lift `.unwrap_or(ProcessPhase::Attested)` — the ONE
1282    ///   site whose default is not `Pending`; the primitive
1283    ///   returns the raw `Option` so the caller's `.unwrap_or`
1284    ///   default choice stays local rather than baked in.
1285    /// * `table_controller::stable_name_group_key` — the routing-
1286    ///   groupby seed that pairs the phase with the PID + creation
1287    ///   timestamp when partitioning Processes claiming the same
1288    ///   stable name. Pre-lift `.unwrap_or(ProcessPhase::Pending)`.
1289    ///
1290    /// All FIVE sites walked the SAME 3-line `.status.as_ref()
1291    /// .map(|s| s.phase)` chain — three closed with `unwrap_or
1292    /// (ProcessPhase::Pending)` (the `Default`), one closed with
1293    /// `unwrap_or(ProcessPhase::Attested)`, one kept the raw
1294    /// `Option<ProcessPhase>` — so the ONE substrate accessor
1295    /// returns the raw `Option<ProcessPhase>` and each consumer
1296    /// keeps its `.unwrap_or(...)` default choice at its own site.
1297    ///
1298    /// A future normalization step (a generation-filter that
1299    /// returns `None` for a phase stamped with a stale
1300    /// `metadata.generation`, a staleness gate that drops a phase
1301    /// whose observing `phase_since` predates a reconcile
1302    /// deadline, a canonicalization pass that maps a phase that
1303    /// no longer belongs to the CRD's closed set to `None`) lands
1304    /// at ONE substrate method here and all five consumers pick
1305    /// up the upgrade mechanically — no per-callsite hand-edit at
1306    /// `reconcile` / `evaluate_process_phase` / `check_depends_on`
1307    /// / `p_current_phase_str` / `stable_name_group_key`.
1308    ///
1309    /// Future status projections (`observed_parent` on the
1310    /// parent-pointer axis, `observed_message` on the human-
1311    /// readable-status axis, `observed_children` on the child
1312    /// fan-out axis, `observed_exit_code` on the terminal-exit
1313    /// axis) land as peer methods on this same axis.
1314    ///
1315    /// Theory anchor: THEORY.md §VI.1 (generation over
1316    /// composition — the 3-line status-projection chain recurred
1317    /// at FIVE hand-authored sites past the ★★ PRIME-DIRECTIVE
1318    /// ≥ 2 duplication trigger, and is lifted to ONE owner here).
1319    /// THEORY.md §II.1 invariant 5 (composition preserves proofs
1320    /// — the pins bind the missing-`status` corner + the
1321    /// per-variant enum round-trip + the byte-identical parity
1322    /// with the pre-lift 3-line chain, so a regression that
1323    /// drifted any surface at `tests::observed_phase_*` rather
1324    /// than as silent operator-facing skew between the
1325    /// controller's dispatch seed and the boundary evaluator's
1326    /// depends-on audit on the SAME `Process` within one
1327    /// reconcile pass).
1328    pub fn observed_phase(&self) -> Option<ProcessPhase> {
1329        self.status.as_ref().map(|s| s.phase)
1330    }
1331
1332    /// The copy-form status-projection primitive on the phase axis
1333    /// with the `Pending` sink applied — the ONE-liner collapse of
1334    /// the paired `self.observed_phase().unwrap_or(ProcessPhase::
1335    /// Pending)` incantation every reconciler consumer restated by
1336    /// hand at the `Option`-flattening tail of the `observed_phase`
1337    /// call. Sibling to [`Self::observed_phase`] on the (return-form
1338    /// × fallback shape) axis pair — the raw-`Option` corner stays
1339    /// as `observed_phase`, this method opens the `Pending`-defaulted
1340    /// corner that four of the five hand-authored `observed_phase`
1341    /// consumers chose (the fifth chose `Attested`; it keeps the raw
1342    /// `Option` accessor because a `Pending` sink would silently drop
1343    /// its released-from-annotation branch into the wrong label).
1344    ///
1345    /// The primitive returns [`ProcessPhase::Pending`] on any missing
1346    /// `status` slot — the same sentinel [`ProcessPhase::default`]
1347    /// returns, and the same fallback all four pre-lift consumers
1348    /// wrote by hand. `ProcessPhase::Pending` is load-bearing as the
1349    /// "not yet observed" default because the top-level dispatcher's
1350    /// `Pending → Forking` transition, the boundary evaluator's
1351    /// per-Process phase-reached postcondition, the routing groupby's
1352    /// stable-name claim-arbiter row seed, and the pool controller's
1353    /// desired-count snapshot all read a freshly-forked Process (no
1354    /// `status` yet stamped by the reconciler) as being at the
1355    /// entrypoint phase of the closed lifecycle. A caller with a
1356    /// different default choice (currently only the SIGSTOP/SIGCONT
1357    /// release gate's `Attested` fallback in
1358    /// `phase_machine::p_current_phase_str`) keeps the raw
1359    /// [`Self::observed_phase`] accessor at its own site.
1360    ///
1361    /// Pre-lift the two-link `.observed_phase().unwrap_or
1362    /// (ProcessPhase::Pending)` chain was hand-authored at FOUR
1363    /// sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold
1364    /// across the workspace:
1365    /// * `tatara-reconciler::controller::reconcile` — the top-level
1366    ///   dispatcher's `current_phase` seed that feeds the
1367    ///   deletion-preempt + signal-ingestion gates + the per-phase
1368    ///   handler dispatch.
1369    /// * `tatara-reconciler::boundary::evaluate_process_phase` — the
1370    ///   boundary evaluator's [`ConditionKind::ProcessPhase`]
1371    ///   evaluator that compares a peer-Process's observed phase
1372    ///   against the operator-declared `phase`-reached postcondition.
1373    /// * `tatara-reconciler::table_controller::stable_name_group_key`
1374    ///   — the routing-groupby seed that pairs the phase with the
1375    ///   PID + creation timestamp when partitioning Processes
1376    ///   claiming the same stable name.
1377    /// * `tatara-pool-reconciler::controller_pool::reconcile_pool` —
1378    ///   the desired-count loop's per-member snapshot seed that feeds
1379    ///   `decide_pool_convergence` with each owned Process's
1380    ///   `(phase, created_at)` pair.
1381    ///
1382    /// All FOUR sites walked the SAME two-link chain and all four
1383    /// closed with `ProcessPhase::Pending` as the sink; post-lift
1384    /// each callsite reads `process.observed_phase_or_pending()` and
1385    /// the produced `ProcessPhase` feeds the same downstream branch
1386    /// (dispatch on the `current_phase` value, comparison against a
1387    /// declared threshold, groupby-key composition, member-state
1388    /// snapshot construction) unchanged.
1389    ///
1390    /// Return-form axis: `ProcessPhase` matches the copy discipline
1391    /// of [`Self::observed_phase`] (a `Copy` scalar one byte wide),
1392    /// with the [`Option`] wrapper collapsed at the primitive rather
1393    /// than at every consumer. A caller that needs the missing-`status`
1394    /// corner as a distinguishable value keeps the raw
1395    /// [`Self::observed_phase`] accessor.
1396    ///
1397    /// A future normalization step (a generation-filter that
1398    /// treats a phase stamped with a stale `metadata.generation` as
1399    /// unobserved and therefore `Pending`, a staleness gate that
1400    /// drops a phase whose observing `phase_since` predates a
1401    /// reconcile deadline, a canonicalization pass that maps a phase
1402    /// that no longer belongs to the CRD's closed set to `Pending`)
1403    /// lands at ONE substrate method here — because this primitive
1404    /// composes on top of [`Self::observed_phase`], the normalization
1405    /// applies to both the raw-`Option` and the `Pending`-sinked
1406    /// return through the SAME upstream body — and all four
1407    /// downstream consumers pick up the upgrade mechanically.
1408    ///
1409    /// Peer to the sibling defaulted-fallback primitive family
1410    /// [`Self::namespace_or_default`] +
1411    /// [`Self::name_or_placeholder`] + [`Self::uid_or_empty`] on the
1412    /// (return-shape × fallback-value) axis — those three open the
1413    /// borrow-form defaulted corner for the metadata slots; this
1414    /// method opens the copy-form defaulted corner for the phase
1415    /// slot on `status`. Future defaulted-fallback status
1416    /// projections (an `observed_pid_or_empty` on the PID axis, an
1417    /// `observed_exit_code_or_zero` on the terminal-exit axis) land
1418    /// as peer methods on this same axis.
1419    ///
1420    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
1421    /// the two-link `.observed_phase().unwrap_or(Pending)` chain
1422    /// recurred at four hand-authored sites past the ★★
1423    /// PRIME-DIRECTIVE ≥ 2 duplication trigger, and is lifted to ONE
1424    /// owner here). THEORY.md §II.1 invariant 5 (composition
1425    /// preserves proofs — the pins bind the missing-`status` sink to
1426    /// `Pending` + populated-status pass-through + every
1427    /// `ProcessPhase` variant round-trip + byte-identical parity
1428    /// with the pre-lift two-link chain, so a regression that
1429    /// drifted any surface at `tests::observed_phase_or_pending_*`
1430    /// rather than as silent operator-facing skew between the
1431    /// top-level dispatcher's `Pending → Forking` seed and the
1432    /// boundary evaluator's per-Process phase-reached postcondition
1433    /// on the SAME `Process` within one reconcile pass).
1434    pub fn observed_phase_or_pending(&self) -> ProcessPhase {
1435        self.observed_phase().unwrap_or(ProcessPhase::Pending)
1436    }
1437
1438    /// The copy-form status-projection primitive on the
1439    /// `status.phase_since` axis: returns the [`DateTime<Utc>`] the
1440    /// reconciler stamped when this Process last transitioned into its
1441    /// current [`ProcessPhase`], wrapped in an `Option` so BOTH the
1442    /// missing-`status` corner AND the empty-slot corner
1443    /// (`ProcessStatus.phase_since == None` — a freshly-forked Process
1444    /// whose reconciler has not yet stamped a first transition) collapse
1445    /// to `None` — the ONE-liner collapse of the paired
1446    /// `self.status.as_ref().and_then(|s| s.phase_since)` incantation
1447    /// the pool reconciler's per-owned-Process member-seed builder
1448    /// restated by hand pre-lift.
1449    ///
1450    /// Pre-lift the 5-line
1451    /// ```rust,ignore
1452    /// p.status
1453    ///     .as_ref()
1454    ///     .and_then(|s| s.phase_since)
1455    ///     .unwrap_or_else(Utc::now)
1456    /// ```
1457    /// chain was hand-authored at
1458    /// `tatara-pool-reconciler::controller_pool::reconcile_inner`'s
1459    /// per-owned-Process `PoolMember { entered_state_at: … }` seed —
1460    /// the row-builder that feeds `pool_phase_from_members` +
1461    /// `apply_pool_reconcile_decision` with each owned Process's
1462    /// last-observed transition instant. Post-lift the callsite reads
1463    /// `p.observed_phase_since().unwrap_or_else(Utc::now)`, a
1464    /// one-liner symmetric to the peer `p.created_at()
1465    /// .unwrap_or_else(Utc::now)` chain the sibling
1466    /// [`PoolMemberSnapshot`] `created_at` seed two branches below
1467    /// already routes through — closing the last raw
1468    /// `.status.as_ref()` chain on `Process` at that reconciler site.
1469    ///
1470    /// Return-form axis: `Option<DateTime<Utc>>` matches the copy-form
1471    /// discipline of the sibling metadata-projection primitive
1472    /// [`Self::created_at`] (both return `Option<DateTime<Utc>>` and
1473    /// hide the wire-format wrapper — `ProcessStatus` on the status
1474    /// side, `k8s_openapi::…::v1::Time` on the metadata side) so the
1475    /// two timestamp-projection primitives compose byte-uniformly at
1476    /// the pool reconciler's `PoolMember` / `PoolMemberSnapshot`
1477    /// seeds. Returning owned `DateTime<Utc>` with a
1478    /// substrate-injected `Utc::now()` fallback would fold an impure
1479    /// wall-clock read into the primitive, breaking the pure-
1480    /// projection discipline every peer `observed_*` accessor
1481    /// follows; the sink stays at the callsite where it composes with
1482    /// [`Self::created_at`]'s identical `.unwrap_or_else(Utc::now)`
1483    /// tail.
1484    ///
1485    /// Peer to the copy-form status-projection primitive
1486    /// [`Self::observed_phase`] on the (return-shape × status-slot)
1487    /// axis pair — both walk the paired `.status.as_ref().<map|and_then>
1488    /// (|s| s.<slot>)` chain and both project a `Copy` inner from a
1489    /// wire slot whose "not yet observed" corner collapses to `None`.
1490    /// [`Self::observed_phase`] projects the `phase` slot (a bare
1491    /// [`ProcessPhase`] with a `Default` sentinel — collapses only on
1492    /// missing `status`); this method projects the `phase_since` slot
1493    /// (an `Option<DateTime<Utc>>` with no sentinel — collapses on
1494    /// missing `status` OR on empty slot). The paired
1495    /// `.map` vs `.and_then` choice tracks the difference: the raw
1496    /// slot is `Option<DateTime<Utc>>` here so the closure returns an
1497    /// `Option` and the outer combinator flattens through `.and_then`,
1498    /// where `observed_phase`'s raw slot is a bare `ProcessPhase` so
1499    /// the closure returns a bare value and the outer combinator maps
1500    /// through `.map`. Future status-timestamp projections (an
1501    /// `observed_last_boundary_check` on
1502    /// [`crate::status::BoundaryStatus.last_check`], an
1503    /// `observed_last_export_receipt` on a future receipt-observation
1504    /// slot) land as peer methods on this same axis.
1505    ///
1506    /// A future normalization step (a per-cluster clock-skew guard
1507    /// that offsets the returned timestamp by the observing controller's
1508    /// measured skew, a canonicalization pass that maps a suspiciously-
1509    /// zero `phase_since` to `None` so consumers' `.unwrap_or_else
1510    /// (Utc::now)` tails synthesize a fresh anchor, a staleness gate
1511    /// that drops a `phase_since` predating a reconcile deadline) lands
1512    /// at ONE substrate method here and every downstream consumer
1513    /// picks up the upgrade mechanically — no per-callsite hand-edit
1514    /// at `reconcile_inner`'s member-seed builder.
1515    ///
1516    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
1517    /// the paired `.status.as_ref().and_then(|s| s.phase_since)` chain
1518    /// closes the last raw `.status.as_ref()` chain in
1519    /// `tatara-pool-reconciler`'s production reconciler code on
1520    /// `Process`, and is lifted to ONE substrate owner here alongside
1521    /// the sibling `observed_phase` / `observed_phase_or_pending` /
1522    /// `observed_identity` / `observed_pid` / `observed_attestation` /
1523    /// `observed_flux_resources` primitives that closed their axes
1524    /// previously). THEORY.md §II.1 invariant 5 (composition preserves
1525    /// proofs — the pins bind the missing-`status` corner + the empty-
1526    /// slot corner + the populated-slot pass-through + the pure-
1527    /// projection discipline + the byte-identical parity with the pre-
1528    /// lift `.status.as_ref().and_then(|s| s.phase_since)` chain + the
1529    /// composition-shape agreement with [`Self::created_at`]'s
1530    /// identical `.unwrap_or_else(Utc::now)` tail at the peer
1531    /// pool-reconciler seed, so a regression that drifted any surface
1532    /// at `tests::observed_phase_since_*` rather than as silent
1533    /// operator-facing skew between the `PoolMember` row's observed-
1534    /// transition anchor and the `PoolMemberSnapshot`'s creation-
1535    /// timestamp anchor on the SAME owned `Process` within one
1536    /// reconcile pass).
1537    #[must_use]
1538    pub fn observed_phase_since(&self) -> Option<DateTime<Utc>> {
1539        self.status.as_ref().and_then(|s| s.phase_since)
1540    }
1541
1542    /// Pure composer over [`Self::observed_phase_since`] that folds the
1543    /// paired `.unwrap_or(fallback)` sink into ONE substrate owner — the
1544    /// ONE-liner collapse of the paired
1545    /// `p.observed_phase_since().unwrap_or_else(Utc::now)` incantation
1546    /// the pool-reconciler consumer restated by hand pre-lift, and the
1547    /// status-slot peer of the sibling [`Self::created_at_or`] composer
1548    /// on the metadata-timestamp axis. The wall-clock read stays at the
1549    /// callsite (as `Utc::now()` passed in positionally) so the composer
1550    /// itself stays pure — matching the discipline every peer `observed_*`
1551    /// / `created_at` copy-form projection follows and the explicit
1552    /// warning against a substrate-injected `Utc::now()` fallback that
1553    /// [`Self::observed_phase_since`]'s doc already spelled out.
1554    ///
1555    /// Pre-lift the paired 2-step
1556    /// `.observed_phase_since().unwrap_or_else(Utc::now)` chain was hand-
1557    /// authored at THREE workspace-wide sites past the ★★ PRIME-DIRECTIVE
1558    /// ≥ 2 duplication threshold, all stamping the SAME wall-clock
1559    /// fallback on the same missing-`phase_since` corner:
1560    /// * `tatara-pool-reconciler::controller_pool::reconcile_inner` —
1561    ///   the per-owned-Process `PoolMember { entered_state_at, .. }`
1562    ///   seed feeding `pool_phase_from_members` +
1563    ///   `apply_pool_reconcile_decision`. A freshly-forked pool member
1564    ///   whose reconciler has not yet stamped a first phase-transition
1565    ///   gets `Utc::now()` synthesized so the observed-transition
1566    ///   anchor sorts as "just entered" rather than short-circuiting
1567    ///   on the missing slot.
1568    /// * `tatara-process::crd::tests::
1569    ///   observed_phase_since_composes_with_unwrap_or_else_utc_now_tail_at_pool_seed`
1570    ///   — the call-site-shape pin that binds the composed tail's
1571    ///   behavior on both the populated corner (fallback silent) and
1572    ///   the empty corner (fallback fires). Two hand-authored
1573    ///   restatements inside the single test pin the same 2-step chain
1574    ///   at fail-before-pass-after granularity.
1575    ///
1576    /// All THREE sites walked the SAME `.unwrap_or_else(Utc::now)` tail
1577    /// on the SAME [`Self::observed_phase_since`] pure projection and
1578    /// all THREE wanted the resolved `DateTime<Utc>` the composer
1579    /// returns. Post-lift each production callsite reads
1580    /// `p.observed_phase_since_or(Utc::now())` and the produced value
1581    /// feeds the same downstream slot unchanged; the test pin retains
1582    /// the pre-lift `.unwrap_or_else(Utc::now)` composition to bind the
1583    /// pure-projection primitive's own byte-identical behavior while a
1584    /// peer test pin binds this composer's byte-identical parity.
1585    ///
1586    /// The `fallback: DateTime<Utc>` parameter (rather than a
1587    /// substrate-injected `Utc::now()`) keeps the composer pure — a
1588    /// test with a fixed-clock harness passes its own frozen anchor, a
1589    /// production consumer passes `Utc::now()`, both go through the
1590    /// same primitive without the composer itself reaching for the
1591    /// wall clock. This resolves exactly the tension
1592    /// [`Self::observed_phase_since`]'s doc spelled out (a buried
1593    /// `Utc::now()` fallback "would fold an impure wall-clock read
1594    /// into the primitive, breaking the pure-projection discipline
1595    /// every peer `observed_*` accessor follows") by lifting the
1596    /// composition shape, not the wall-clock read.
1597    ///
1598    /// Return-form axis: `DateTime<Utc>` matches the `unwrap_or`-style
1599    /// composer discipline of `Option::unwrap_or` in std — takes the
1600    /// pure projection, an owned fallback, returns the resolved owned
1601    /// value. Peer to the substrate composer [`Self::created_at_or`]
1602    /// on the metadata-timestamp axis — both lift a
1603    /// `.unwrap_or(<fallback>)` tail into ONE substrate site so the
1604    /// fallback-shape decision lives at ONE owner per axis; the two
1605    /// timestamp-projection composers on `Process` (metadata-timestamp
1606    /// [`Self::created_at_or`] + status-timestamp [`Self`]) now
1607    /// compose byte-uniformly at the pool reconciler's per-member row
1608    /// builder, closing the last hand-authored `.unwrap_or_else(Utc::
1609    /// now)` tail on `Process` in that reconciler's production code.
1610    ///
1611    /// A future normalization step (a per-cluster clock-skew guard
1612    /// that offsets the returned timestamp by the observing
1613    /// controller's measured skew before applying the fallback, a
1614    /// canonicalization pass that folds a suspiciously-zero
1615    /// `phase_since` to the fallback rather than accepting it, a
1616    /// staleness gate that treats a `phase_since` predating a
1617    /// reconcile deadline as unobserved and falls through to the
1618    /// fallback) lands at ONE substrate method here and every
1619    /// downstream consumer picks up the upgrade mechanically — no
1620    /// per-callsite hand-edit at
1621    /// `controller_pool::reconcile_inner`'s member-seed builder or at
1622    /// any future observed-transition consumer (a stable-name claim-
1623    /// arbiter age tie-break on the status-transition anchor, a
1624    /// per-pool dwell-time reap probe on the same slot).
1625    ///
1626    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
1627    /// the paired `.observed_phase_since().unwrap_or_else(Utc::now)`
1628    /// chain recurred at three hand-authored sites past the ★★
1629    /// PRIME-DIRECTIVE ≥ 2 duplication trigger, and is lifted to ONE
1630    /// owner here alongside the sibling [`Self::created_at_or`] on
1631    /// the metadata-timestamp axis). THEORY.md §II.1 invariant 5
1632    /// (composition preserves proofs — the pins bind the missing-slot
1633    /// fallback corner + the populated-slot pass-through + the pure-
1634    /// composer discipline + the byte-identical parity with the
1635    /// pre-lift `.unwrap_or(fallback)` chain, so a regression that
1636    /// drifted any surface at `tests::observed_phase_since_or_*`
1637    /// rather than as silent operator-facing skew between the
1638    /// `PoolMember` row's observed-transition anchor and any future
1639    /// observed-transition consumer on the SAME `Process` within one
1640    /// reconcile pass).
1641    #[must_use]
1642    pub fn observed_phase_since_or(&self, fallback: DateTime<Utc>) -> DateTime<Utc> {
1643        self.observed_phase_since().unwrap_or(fallback)
1644    }
1645
1646    /// Copy-form metadata-projection primitive on the deletion-tombstone
1647    /// axis: returns `true` iff the K8s API server has stamped a
1648    /// `metadata.deletionTimestamp` on this Process (the moment the
1649    /// object entered the "being deleted" corner of its lifecycle,
1650    /// after which further mutating writes are refused and finalizers
1651    /// are drained before the object is actually removed) — the ONE-
1652    /// liner collapse of the paired `self.metadata.deletion_timestamp
1653    /// .is_some()` incantation every consumer restated by hand
1654    /// pre-lift.
1655    ///
1656    /// Pre-lift the `.metadata.deletion_timestamp.is_some()` chain
1657    /// was hand-authored at TWO sites past the ★★ PRIME-DIRECTIVE
1658    /// ≥ 2 duplication threshold in `tatara-reconciler`, both
1659    /// projecting the SAME tombstone-presence predicate on a
1660    /// `Process` value:
1661    /// * `controller::reconcile` — the top-level dispatcher's
1662    ///   deletion-preempt gate that forces the SIGTERM cascade
1663    ///   (`→ Exiting`) as soon as the API server stamps the
1664    ///   tombstone, before the phase handler for the current
1665    ///   [`ProcessPhase`] gets a chance to run. Composed with
1666    ///   [`ProcessPhase::is_alive`] so the preempt only fires on a
1667    ///   Process still in an alive phase — a Process already in
1668    ///   `Zombie` / `Reaped` / `Failed` runs its normal handler.
1669    /// * `phase_machine::handle_exiting` — the SIGTERM cascade's
1670    ///   child-fan-out loop that enumerates every child Process and
1671    ///   skips ones the API server has already tombstoned (so the
1672    ///   reconciler does not re-issue a `DELETE` against a child
1673    ///   whose deletion the API server is already draining through
1674    ///   its own finalizer). The skip composes with
1675    ///   [`Self::coordinates_or_none`]'s name-required probe so a
1676    ///   child missing either its tombstone-absent gate or its
1677    ///   `metadata.name` slot is a clean `continue` rather than an
1678    ///   attempted `child_api.delete("")` no-op.
1679    ///
1680    /// Both sites walked the SAME `.metadata.deletion_timestamp
1681    /// .is_some()` chain and both wanted the `bool` form the
1682    /// primitive returns — the `controller::reconcile` site to gate
1683    /// the SIGTERM preempt with `&& current_phase.is_alive()` and
1684    /// the `handle_exiting` site to gate the DELETE-skip with a
1685    /// bare `if child.is_being_deleted() { continue; }`. Post-lift
1686    /// each callsite reads `process.is_being_deleted()` and the
1687    /// produced `bool` feeds the same downstream gate unchanged.
1688    ///
1689    /// Return-form axis: `bool` matches the copy-form discipline of
1690    /// [`Self::observed_phase`] (an `Option<Copy>` scalar) — the
1691    /// underlying slot is a wire-format `Option<Time>` that carries
1692    /// only presence information at this axis (the RFC-3339 timestamp
1693    /// payload itself is not what the two consumers read; both only
1694    /// probe presence to detect the tombstone-stamped state).
1695    /// Returning the raw `Option<&Time>` would push the `.is_some()`
1696    /// probe back to every callsite, restating the pre-lift chain
1697    /// one link shorter without collapsing the primitive.
1698    ///
1699    /// Peer to the metadata-fallback primitives
1700    /// [`Self::namespace_or_default`], [`Self::name_or_placeholder`],
1701    /// [`Self::uid_or_empty`], [`Self::coordinates_or_defaults`],
1702    /// [`Self::coordinates_or_none`], [`Self::owned_coordinates_or_err`],
1703    /// [`Self::annotation`] on the metadata axis; this method opens
1704    /// the copy-form peer for the presence-probe corner. Future
1705    /// metadata-presence projections (an `is_being_finalized`
1706    /// projection on `metadata.finalizers.is_empty()`'s negation,
1707    /// a `has_owner` projection on `metadata.owner_references.is_empty()`'s
1708    /// negation) land as peer methods on this same axis.
1709    ///
1710    /// A future normalization step (a per-tombstone staleness gate
1711    /// that returns `false` for a tombstone older than the reconciler's
1712    /// grace-period budget, a canonicalization pass that treats a
1713    /// tombstone from a paused controller as absent, a cross-cluster
1714    /// tombstone-observation clock skew guard) lands at ONE substrate
1715    /// method here and both downstream consumers pick up the upgrade
1716    /// mechanically — no per-callsite hand-edit at
1717    /// `controller::reconcile` / `phase_machine::handle_exiting`.
1718    ///
1719    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
1720    /// the `.metadata.deletion_timestamp.is_some()` chain recurred at
1721    /// two hand-authored sites past the ★★ PRIME-DIRECTIVE ≥ 2
1722    /// duplication trigger, and is lifted to ONE owner here).
1723    /// THEORY.md §II.1 invariant 5 (composition preserves proofs —
1724    /// the pins bind the missing-tombstone corner + the present-
1725    /// tombstone corner + the copy-form `bool` return + the byte-
1726    /// identical parity with the pre-lift `.is_some()` chain, so a
1727    /// regression that drifted any surface at
1728    /// `tests::is_being_deleted_*` rather than as silent operator-
1729    /// facing skew between the top-level dispatcher's SIGTERM
1730    /// preempt and the SIGTERM cascade's child-fan-out DELETE-skip
1731    /// on the SAME `Process` within one reconcile pass).
1732    pub fn is_being_deleted(&self) -> bool {
1733        self.metadata.deletion_timestamp.is_some()
1734    }
1735
1736    /// Copy-form metadata-projection primitive on the
1737    /// `metadata.creationTimestamp` axis: returns the K8s-API-server-
1738    /// assigned creation moment as a `DateTime<Utc>`, hiding the wire-
1739    /// format `k8s_openapi::apimachinery::pkg::apis::meta::v1::Time`
1740    /// newtype behind an inherent projection — the ONE-liner collapse
1741    /// of the paired `self.metadata.creation_timestamp.as_ref().map(|t|
1742    /// t.0)` incantation every timestamp-driven consumer restated by
1743    /// hand pre-lift.
1744    ///
1745    /// Pre-lift the paired `.metadata.creation_timestamp.as_ref()` +
1746    /// `t.0` unwrap chain was hand-authored at THREE sites past the
1747    /// ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold across the
1748    /// workspace, all projecting the SAME creation-moment `DateTime<Utc>`
1749    /// on a `Process`:
1750    /// * `tatara-process::lifetime_clock::evaluate` — TTL-expiry gate
1751    ///   in the ephemeral-lifetime decision (`elapsed = now
1752    ///   .signed_duration_since(creation.0)`), inside the non-terminal-
1753    ///   phase guard that fires the `AutoTerminate::Now { TtlExpired }`
1754    ///   branch. Pre-lift the site read `if let Some(creation) = process
1755    ///   .metadata.creation_timestamp.as_ref() { ... creation.0 ... }`.
1756    /// * `tatara-process::lifetime_clock::requeue_with_ttl` — sleep-
1757    ///   budget picker for the reconciler's next requeue, choosing the
1758    ///   smaller of HEARTBEAT and TTL-remaining so the reconciler
1759    ///   doesn't oversleep past a TTL boundary. Pre-lift the site read
1760    ///   `let Some(creation) = process.metadata.creation_timestamp
1761    ///   .as_ref() else { return default; };` + `creation.0`.
1762    /// * `tatara-reconciler::table_controller::reconcile_process_table`
1763    ///   — stable-name claim-arbiter row builder, seeding each
1764    ///   candidate row's `created_at` for the tie-break ordering
1765    ///   (oldest wins). Pre-lift the site read `p.metadata
1766    ///   .creation_timestamp.as_ref().map(|t| t.0).unwrap_or_else(Utc
1767    ///   ::now)`.
1768    ///
1769    /// All THREE sites walked the SAME two-link chain — read the
1770    /// `Option<Time>` slot as a borrow, then unwrap the `Time` newtype
1771    /// to its inner `DateTime<Utc>` — differing only in the tail
1772    /// (`if-let-Some` guard, `let-else` short-circuit, `Utc::now`
1773    /// fallback). Post-lift each callsite reads
1774    /// `process.created_at()` and applies its own tail at its own site
1775    /// (`if let Some(creation) = ...`, `let Some(creation) = ... else`,
1776    /// `.unwrap_or_else(Utc::now)`).
1777    ///
1778    /// Return-form axis: `Option<DateTime<Utc>>` matches the copy-form
1779    /// discipline of the sibling status-projection primitive
1780    /// [`Self::observed_phase`] — both return `Option<T>` where `T:
1781    /// Copy` and hide the wire-format wrapper (`ProcessStatus` on the
1782    /// status side; `Time` on the metadata side). Returning the raw
1783    /// `Option<&Time>` would push the `.0` unwrap back to every
1784    /// callsite, restating the pre-lift chain one link shorter without
1785    /// collapsing the primitive; returning owned `Option<Time>` would
1786    /// force a `Time` import at every consumer for a projection every
1787    /// consumer immediately discards past `.0`.
1788    ///
1789    /// Peer to the metadata-fallback + presence-probe primitives
1790    /// [`Self::namespace_or_default`], [`Self::name_or_placeholder`],
1791    /// [`Self::uid_or_empty`], [`Self::coordinates_or_defaults`],
1792    /// [`Self::coordinates_or_none`], [`Self::owned_coordinates_or_err`],
1793    /// [`Self::annotation`], [`Self::is_being_deleted`] on the metadata
1794    /// axis; this method opens the copy-form timestamp corner. Future
1795    /// metadata-timestamp projections (a
1796    /// `deletion_at() -> Option<DateTime<Utc>>` peer on the
1797    /// tombstone-payload axis for staleness gates that need the
1798    /// timestamp value alongside the presence bit) land as peer
1799    /// methods on this same axis.
1800    ///
1801    /// A future normalization step (a per-cluster clock-skew guard
1802    /// that offsets the returned timestamp by the observing controller's
1803    /// measured skew, a canonicalization pass that maps a suspiciously-
1804    /// zero creation moment to `None`, a per-namespace override that
1805    /// substitutes a `spec.identity`-declared creation anchor for the
1806    /// metadata slot on adopted resources) lands at ONE substrate
1807    /// method here and all three downstream consumers pick up the
1808    /// upgrade mechanically — no per-callsite hand-edit at `evaluate`
1809    /// / `requeue_with_ttl` / `reconcile_process_table`.
1810    ///
1811    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
1812    /// the `.metadata.creation_timestamp.as_ref().map(|t| t.0)` chain
1813    /// recurred at three hand-authored sites past the ★★
1814    /// PRIME-DIRECTIVE ≥ 2 duplication trigger, and is lifted to ONE
1815    /// owner here). THEORY.md §II.1 invariant 5 (composition preserves
1816    /// proofs — the pins bind the missing-timestamp corner + the
1817    /// present-timestamp corner + the copy-form `DateTime<Utc>` return
1818    /// + the byte-identical parity with the pre-lift `.as_ref().map(|t|
1819    /// t.0)` chain, so a regression that drifted any surface at
1820    /// `tests::created_at_*` rather than as silent operator-facing
1821    /// skew between the TTL-expiry gate, the requeue-budget picker,
1822    /// and the stable-name claim-arbiter tie-break on the SAME
1823    /// `Process` within one reconcile pass).
1824    pub fn created_at(&self) -> Option<DateTime<Utc>> {
1825        self.metadata.creation_timestamp.as_ref().map(|t| t.0)
1826    }
1827
1828    /// Pure composer over [`Self::created_at`] that folds the paired
1829    /// `.unwrap_or(fallback)` sink into ONE substrate owner — the
1830    /// ONE-liner collapse of the paired
1831    /// `p.created_at().unwrap_or_else(Utc::now)` incantation the two
1832    /// production consumers restated by hand pre-lift, with the
1833    /// wall-clock read kept at the callsite (as `Utc::now()` passed in
1834    /// positionally) so the primitive itself stays pure — matching the
1835    /// discipline every peer `observed_*` / `created_at` copy-form
1836    /// projection follows and the explicit warning against a
1837    /// substrate-injected `Utc::now()` fallback that
1838    /// [`Self::observed_phase_since`]'s doc already spelled out.
1839    ///
1840    /// Pre-lift the paired 2-step
1841    /// `.created_at().unwrap_or_else(Utc::now)` chain was hand-authored
1842    /// at TWO production sites past the ★★ PRIME-DIRECTIVE ≥ 2
1843    /// duplication threshold, both stamping the SAME wall-clock
1844    /// fallback on the same missing-timestamp corner:
1845    /// * `tatara-reconciler::table_controller::reconcile_process_table` —
1846    ///   the per-Process claim-row's `created_at` anchor that feeds
1847    ///   the stable-name group's tie-break comparator; a freshly-forked
1848    ///   Process whose API server has not yet stamped
1849    ///   `metadata.creationTimestamp` gets `Utc::now()` synthesized so
1850    ///   the tie-break sorts by "just-created" order rather than
1851    ///   short-circuiting on the missing slot.
1852    /// * `tatara-pool-reconciler::controller_pool::reconcile_inner`'s
1853    ///   desired-count `PoolMemberSnapshot { created_at, .. }` seed —
1854    ///   the per-owned-Process snapshot fed to
1855    ///   `decide_pool_convergence`, whose stability arithmetic
1856    ///   subtracts the anchor from `now` to compute the observed dwell
1857    ///   time; the same "just-created" fallback keeps a freshly-spawned
1858    ///   pool member from being reaped as if it were a stale zombie.
1859    ///
1860    /// Both sites walked the SAME `.unwrap_or_else(Utc::now)` tail on
1861    /// the SAME [`Self::created_at`] pure projection and both wanted
1862    /// the resolved `DateTime<Utc>` the composer returns. Post-lift
1863    /// each callsite reads `p.created_at_or(Utc::now())` and the
1864    /// produced value feeds the same downstream slot unchanged.
1865    ///
1866    /// The `fallback: DateTime<Utc>` parameter (rather than a
1867    /// substrate-injected `Utc::now()`) keeps the composer pure — a
1868    /// test with a fixed-clock harness passes its own frozen anchor, a
1869    /// production consumer passes `Utc::now()`, both go through the
1870    /// same primitive without the composer itself reaching for the
1871    /// wall clock. This resolves the tension the sibling
1872    /// [`Self::observed_phase_since`]'s doc spelled out (a buried
1873    /// `Utc::now()` fallback "would fold an impure wall-clock read
1874    /// into the primitive, breaking the pure-projection discipline
1875    /// every peer `observed_*` accessor follows") by lifting the
1876    /// composition shape, not the wall-clock read.
1877    ///
1878    /// Return-form axis: `DateTime<Utc>` matches the `unwrap_or`-style
1879    /// composer discipline of `Option::unwrap_or` in std — takes the
1880    /// pure projection, an owned fallback, returns the resolved owned
1881    /// value. Peer to the substrate composers
1882    /// [`Self::observed_phase_or_pending`] on the status-phase axis and
1883    /// [`Self::coordinates_or_defaults`] on the metadata-coordinate
1884    /// axis; all three lift a `.unwrap_or(<fallback>)` tail into ONE
1885    /// substrate site so the fallback-shape decision lives at ONE
1886    /// owner per axis.
1887    ///
1888    /// A future normalization step (a per-cluster clock-skew guard
1889    /// that offsets the returned timestamp by the observing
1890    /// controller's measured skew before applying the fallback, a
1891    /// canonicalization pass that folds a suspiciously-zero
1892    /// `creationTimestamp` to the fallback rather than accepting it,
1893    /// a per-namespace override that substitutes a `spec.identity`-
1894    /// declared creation anchor for the metadata slot on adopted
1895    /// resources) lands at ONE substrate method here and both
1896    /// downstream consumers pick up the upgrade mechanically — no
1897    /// per-callsite hand-edit at `reconcile_process_table` /
1898    /// `reconcile_inner`.
1899    ///
1900    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
1901    /// the paired `.created_at().unwrap_or_else(Utc::now)` chain
1902    /// recurred at two hand-authored sites past the ★★
1903    /// PRIME-DIRECTIVE ≥ 2 duplication trigger, and is lifted to ONE
1904    /// owner here). THEORY.md §II.1 invariant 5 (composition
1905    /// preserves proofs — the pins bind the missing-slot fallback
1906    /// corner + the populated-slot pass-through + the pure-composer
1907    /// discipline + the byte-identical parity with the pre-lift
1908    /// `.unwrap_or(fallback)` chain, so a regression that drifted
1909    /// any surface at `tests::created_at_or_*` rather than as
1910    /// silent operator-facing skew between the claim-arbiter's
1911    /// tie-break anchor and the pool convergence snapshot's dwell-time
1912    /// anchor on the SAME `Process` within one reconcile pass).
1913    #[must_use]
1914    pub fn created_at_or(&self, fallback: DateTime<Utc>) -> DateTime<Utc> {
1915        self.created_at().unwrap_or(fallback)
1916    }
1917
1918    /// Wall-clock-anchored peer of [`Self::created_at_or`] — reads
1919    /// `Utc::now()` at call time and forwards it into the pure composer's
1920    /// `fallback` slot so the wall-clock projection lives at ONE
1921    /// substrate site rather than at each production callsite.
1922    ///
1923    /// # Why it exists
1924    ///
1925    /// Pre-lift the 2-arg `p.created_at_or(Utc::now())` chain was
1926    /// hand-authored at TWO production sites past the ★★ PRIME-DIRECTIVE
1927    /// ≥ 2 duplication threshold, each pairing the pure
1928    /// [`Self::created_at_or`] composer with a `Utc::now()` fallback
1929    /// argument at a per-Process anchor seed:
1930    ///
1931    /// * `tatara-reconciler::table_controller::reconcile_process_table`
1932    ///   — the per-Process claim-row `created_at` anchor feeding the
1933    ///   stable-name group's tie-break comparator; a freshly-forked
1934    ///   Process whose API server has not yet stamped
1935    ///   `metadata.creationTimestamp` gets the wall-clock read
1936    ///   synthesized so the tie-break sorts by "just-created" order
1937    ///   rather than short-circuiting on the missing slot.
1938    /// * `tatara-pool-reconciler::controller_pool::reconcile_inner`'s
1939    ///   desired-count `PoolMemberSnapshot { created_at, .. }` seed —
1940    ///   the per-owned-Process snapshot fed to
1941    ///   `decide_pool_convergence`, whose stability arithmetic
1942    ///   subtracts the anchor from `now` to compute observed dwell
1943    ///   time; the same "just-created" wall-clock fallback keeps a
1944    ///   freshly-spawned pool member from being reaped as a stale
1945    ///   zombie.
1946    ///
1947    /// Both sites walked the SAME 2-arg call with the SAME `Utc::now()`
1948    /// fallback — the wall-clock projection had no per-callsite
1949    /// variation. Post-lift both consumers share ONE substrate owner
1950    /// for the wall-clock-at-tick projection; a future clock swap (a
1951    /// monotonic clock cross-check, a per-reconciler injected time
1952    /// source, a test-only override at the production callsite via
1953    /// feature flag) lands at ONE substrate function and both anchor
1954    /// seeds inherit the upgrade mechanically.
1955    ///
1956    /// The 2-arg [`Self::created_at_or`] peer stays load-bearing for
1957    /// this crate's own test suite — the injected-`fallback` shape is
1958    /// what unit tests use to drive the fallback anchor deterministically
1959    /// (every `p.created_at_or(seeded_anchor)` in the pin family below
1960    /// reads that surface). This peer is production-only: pinning the
1961    /// wall-clock at the substrate site means no test can accidentally
1962    /// consume `created_at_or_now` without the deterministic-clock
1963    /// injection that makes the test meaningful.
1964    ///
1965    /// Sibling of the wall-clock-anchored peer family across the
1966    /// workspace's timed-decision axes:
1967    /// [`crate::pool::PoolStatus::observed_now`] on the
1968    /// `PoolStatus`-observation axis,
1969    /// [`crate::allocation::AllocationStatus::transition_now`] on the
1970    /// `AllocationStatus`-transition axis, and
1971    /// [`crate::lifetime_clock::evaluate_now`] on the
1972    /// `AutoTerminate` timed-decision axis. All four primitives own the
1973    /// "read the wall clock at tick-time" projection on a peer
1974    /// clock-injectable pure composer so the workspace's
1975    /// wall-clock-anchored peer family stays uniform across every
1976    /// production callsite.
1977    ///
1978    /// # Invariants
1979    ///
1980    /// - **Same shape:** returns the SAME `DateTime<Utc>` the 2-arg
1981    ///   [`Self::created_at_or`] returns when passed `Utc::now()` as
1982    ///   the fallback argument. This is a delegation, not a
1983    ///   re-implementation.
1984    /// - **Wall-clock read once:** `Utc::now()` is called exactly ONCE
1985    ///   per invocation, at the primitive's body, so a future consumer
1986    ///   that chains two `created_at_or_now` calls back-to-back still
1987    ///   sees monotonic `now` reads (each call reads a fresh instant,
1988    ///   not a cached one) — matches the pre-lift shape where each of
1989    ///   the two anchor sites computed its own `Utc::now()` at its own
1990    ///   line.
1991    ///
1992    /// # `#[must_use]`
1993    ///
1994    /// Every consumer feeds the returned `DateTime<Utc>` into a
1995    /// downstream slot (`ClaimRecord.created_at`, `PoolMemberSnapshot
1996    /// .created_at`). Dropping the return means the anchor was
1997    /// computed for no observable reason — the attribute surfaces that
1998    /// as a warning at every call site.
1999    ///
2000    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
2001    /// the 2-arg call with `Utc::now()` as the fallback argument
2002    /// recurred at 2 hand-authored sites past the ★★ PRIME-DIRECTIVE
2003    /// ≥ 2 duplication trigger, lifted onto the ONE workspace-wide
2004    /// substrate owner here). THEORY.md §II.1 invariant 5 (composition
2005    /// preserves proofs — the wall-clock projection lives at ONE site
2006    /// so a future clock swap reaches both consumers through one
2007    /// edit).
2008    #[must_use]
2009    pub fn created_at_or_now(&self) -> DateTime<Utc> {
2010        self.created_at_or(Utc::now())
2011    }
2012
2013    /// Compound spec-projection primitive on the `spec.lifetime` axis:
2014    /// returns `Some(&e)` iff the resolver unambiguously picks the
2015    /// `Ephemeral` slot, `None` otherwise — the ONE-liner collapse of
2016    /// the 4-step `self.spec.lifetime.resolved_ephemeral()` chain the
2017    /// two `lifetime_clock` consumers previously reached through and
2018    /// the coherence-tightening lift of the naked
2019    /// `self.spec.lifetime.ephemeral.as_ref()` raw-field access
2020    /// `tatara_reconciler::render::render_export_jobs` previously
2021    /// walked past.
2022    ///
2023    /// Pre-lift THREE consumer sites past the ★★ PRIME-DIRECTIVE ≥ 2
2024    /// duplication threshold reached the ephemeral inner through TWO
2025    /// different chains that disagreed on the ambiguous corner:
2026    /// * `tatara_process::lifetime_clock::evaluate` — ambiguity-aware:
2027    ///   `process.spec.lifetime.resolved_ephemeral()` collapses BOTH-
2028    ///   slots-set to `None`, matching the "no ephemeral action"
2029    ///   outcome (`AutoTerminate::Skip`) the ambiguous case must yield.
2030    /// * `tatara_process::lifetime_clock::requeue_with_ttl` —
2031    ///   ambiguity-aware peer of `evaluate`; both share the SAME
2032    ///   `resolved_ephemeral()` gate and MUST agree on the ambiguous
2033    ///   corner or the reconciler's teardown decision and requeue-
2034    ///   budget picker drift apart on the SAME `Process` within one
2035    ///   reconcile pass.
2036    /// * `tatara_reconciler::render::render_export_jobs` — RAW field
2037    ///   access: `process.spec.lifetime.ephemeral.as_ref()` returned
2038    ///   `Some(&e)` on the ambiguous corner, so an operator-authored
2039    ///   `Process` with BOTH `permanent:` AND `ephemeral:` slots
2040    ///   populated would emit export Jobs whose teardown-triggered
2041    ///   fire semantics `lifetime_clock` refused to honor. The two
2042    ///   consumers drifted at the mis-configuration corner.
2043    ///
2044    /// Post-lift ALL THREE consumers reach through ONE `Process` method
2045    /// that composes `self.spec.lifetime.resolved_ephemeral()` — the
2046    /// ambiguity-aware `variant().ok() + as_ephemeral` chain
2047    /// [`crate::lifetime::Lifetime::resolved_ephemeral`] owns — and
2048    /// the drift between the reconciler's export-render arm and the
2049    /// lifetime clock's teardown/TTL arm CLOSES at ONE substrate site.
2050    ///
2051    /// Return-form axis: `Option<&EphemeralLifetime>` matches the
2052    /// borrow-form discipline of the underlying
2053    /// [`crate::lifetime::Lifetime::resolved_ephemeral`] projection so
2054    /// the borrow carries the `'_self` lifetime through directly
2055    /// without a temporary `LifetimeVariant` binding. Peer to the
2056    /// borrow-form status-projection primitives
2057    /// [`Self::observed_attestation`], [`Self::observed_identity`] and
2058    /// the borrow-form metadata-projection primitive
2059    /// [`Self::uid_or_empty`] — all four hide a wrapping `Option`-
2060    /// carrying wire slot behind an inherent projection.
2061    ///
2062    /// A future normalization step (a canonicalization pass that maps
2063    /// a suspiciously-zero `ttl` to a per-cluster default, a per-
2064    /// namespace override that substitutes an operator-declared
2065    /// teardown policy on adopted resources, a wire-schema migration
2066    /// that renames `spec.lifetime.ephemeral` to `spec.lifetime.timed`
2067    /// with a bridging `From` shim) lands at ONE substrate method here
2068    /// and all three downstream consumers pick up the upgrade
2069    /// mechanically — no per-callsite hand-edit at `evaluate` /
2070    /// `requeue_with_ttl` / `render_export_jobs`.
2071    ///
2072    /// Theory anchor: THEORY.md §II.1 invariant 5 (composition
2073    /// preserves proofs — the pins bind the Permanent-only corner, the
2074    /// Ephemeral-only corner, the Both-set-ambiguous corner, the
2075    /// empty-default corner, and the byte-identity parity with the
2076    /// underlying `self.spec.lifetime.resolved_ephemeral()` delegate,
2077    /// so a regression that silently swapped the projection back to
2078    /// the raw `.ephemeral.as_ref()` field would surface here rather
2079    /// than as operator-facing drift between the export-render arm
2080    /// and the teardown/TTL arm on the SAME `Process`). THEORY.md
2081    /// §VI.1 (generation over composition — the ambiguity-aware
2082    /// projection recurred at three hand-authored sites past the ★★
2083    /// PRIME-DIRECTIVE ≥ 2 duplication trigger, and is lifted to ONE
2084    /// owner here).
2085    pub fn resolved_ephemeral(&self) -> Option<&EphemeralLifetime> {
2086        self.spec.lifetime.resolved_ephemeral()
2087    }
2088}
2089
2090impl ProcessSpec {
2091    /// Canonical minimum [`ProcessSpec`] — a [`Classification::gate_compute`]
2092    /// classification with every other field parked at its [`Default`] —
2093    /// the workspace-baseline spec every consumer that needed "a
2094    /// `ProcessSpec` that just exists, with no domain-specific claim on
2095    /// intent / boundary / lifetime / routing / encapsulates" hand-authored
2096    /// as a 12-line struct-literal at scattered sites across the workspace.
2097    ///
2098    /// The composition is the two-primitive product of
2099    /// [`Classification::gate_compute`] (the two axes with no `Default` — a
2100    /// `Gate` point on the `Compute` substrate) with the `Default` impl on
2101    /// every other slot: [`IdentitySpec`], [`Intent`], [`Boundary`],
2102    /// [`ComplianceSpec`], `Vec<DependsOn>`, [`SignalPolicy`], [`Lifetime`],
2103    /// `Option<RoutingSpec>`, `Option<EncapsulatesSpec>`, `bool`. The 11
2104    /// defaulted axes ride at the sibling closed-set + `#[serde(default)]`
2105    /// defaults the CRD already owns; the two `_or_default` /
2106    /// `_or_placeholder` corners on the metadata axis stay closed at the
2107    /// substrate ([`Process::coordinates_or_defaults`],
2108    /// [`Process::name_or_placeholder`]) since this primitive builds the
2109    /// `spec` half, not the `metadata` half.
2110    ///
2111    /// Pre-lift the 12-line `ProcessSpec { identity: <Default>,
2112    /// classification: Classification::gate_compute(), intent: <Default>,
2113    /// boundary: Default::default(), compliance: Default::default(),
2114    /// depends_on: vec![], signals: Default::default(), lifetime:
2115    /// Default::default(), routing: None, encapsulates: None, suspended:
2116    /// false }` struct-literal recurred at EIGHT hand-authored sites past
2117    /// the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold across four
2118    /// crates, each restating the SAME 12-slot verbatim:
2119    /// * `tatara-process::crd::tests::empty_spec` — the substrate test
2120    ///   fixture that pins every `Process::*_or_*` metadata-projection
2121    ///   primitive on the (return-form × fallback-shape) axis;
2122    /// * `tatara-process::lib::tests::empty_process_spec` (×2) — the
2123    ///   sibling fixture inside the `qualified_process_ref` +
2124    ///   `DeletionTombstoned` / `Annotated` trait pin modules;
2125    /// * `tatara-process::lib::tests` (one inline site in the
2126    ///   `qualified_process_ref_composes_from_process_coordinates_or_defaults`
2127    ///   pin) — restated the SAME 12-line block inside the test body;
2128    /// * `tatara-reconciler::claim::tests::empty_process` — the claim-
2129    ///   arbiter row-builder pin fixture;
2130    /// * `tatara-pool-reconciler::controller_pool::tests` (×3) — the
2131    ///   `empty_spec` fixture + two inline `process_to_member_state_*` pin
2132    ///   sites that hand-composed the same 12-slot spec inline.
2133    ///
2134    /// Five more sites walked the SAME 12-slot shape but overrode ONE
2135    /// field (intent, lifetime, or routing) inline and are lifted onto
2136    /// the primitive via struct-update syntax
2137    /// (`..ProcessSpec::gate_compute_defaults()`): the three
2138    /// `tatara-reconciler::render` test-fixture helpers
2139    /// (`render_through_top_level_intent_dispatch`, `process_with`,
2140    /// `demo_process`) and the two `tatara-process::lifetime_clock`
2141    /// helpers (`ephemeral_process`, `permanent_process`).
2142    ///
2143    /// Post-lift each callsite reads `ProcessSpec::gate_compute_defaults()`
2144    /// (or `ProcessSpec { <slot>: <value>,
2145    /// ..ProcessSpec::gate_compute_defaults() }` for the override sites);
2146    /// a future workspace-wide baseline shift (a new `#[serde(default)]`
2147    /// on a promoted [`Intent`] variant, a rename of a defaulted slot, a
2148    /// per-baseline compliance overlay stamping through the spec) lands
2149    /// at ONE substrate function here and every downstream consumer
2150    /// inherits the upgrade mechanically. The current pin ties the
2151    /// classification axis to the sibling [`Classification::gate_compute`]
2152    /// primitive so a future change to that baseline surfaces at this
2153    /// primitive's tests rather than as silent drift across thirteen
2154    /// independent callsites.
2155    ///
2156    /// Sibling to [`Classification::gate_compute`] on the composition-
2157    /// depth axis — that primitive owns the ONE-axis-slice construction
2158    /// (the 5-slot [`Classification`] value); this primitive owns the
2159    /// FULL-spec construction (the 11-slot [`ProcessSpec`] value that
2160    /// wraps the classification-slice plus every other slot at
2161    /// `Default`). A future peer `ProcessSpec::observability_stack()` or
2162    /// similar named variant lands as a sibling method here when a
2163    /// second unremarkable-baseline shape opens.
2164    ///
2165    /// Theory anchor: THEORY.md §VI.1 (generation over composition — the
2166    /// 12-line struct-literal shape recurred at EIGHT hand-authored sites
2167    /// past the ★★ PRIME-DIRECTIVE ≥ 2 duplication trigger and is lifted
2168    /// onto ONE workspace-wide owner here). THEORY.md §II.1 invariant 5
2169    /// (composition preserves proofs — a regression that drifted the
2170    /// baseline axis choice at only one consumer, or that broke the
2171    /// sibling-default correspondence with [`Classification::gate_compute`],
2172    /// surfaces at this primitive's tests rather than as silent operator-
2173    /// visible skew between the eight exact-match test-fixtures + the
2174    /// five override sites whose struct-update composition depends on the
2175    /// shape).
2176    #[must_use]
2177    pub fn gate_compute_defaults() -> Self {
2178        Self {
2179            identity: IdentitySpec::default(),
2180            classification: Classification::gate_compute(),
2181            intent: Intent::default(),
2182            boundary: Boundary::default(),
2183            compliance: ComplianceSpec::default(),
2184            depends_on: Vec::new(),
2185            signals: SignalPolicy::default(),
2186            lifetime: Lifetime::default(),
2187            routing: None,
2188            encapsulates: None,
2189            suspended: false,
2190        }
2191    }
2192
2193    /// [`Self::gate_compute_defaults`] with `axis` overlaid onto the
2194    /// classification slot via [`ClassificationAxis::overlay`] — the peer
2195    /// (spec × axis-slice) composer of
2196    /// [`Classification::gate_compute_with_axis`] on the (Classification ×
2197    /// axis-slice) construction axis.
2198    ///
2199    /// Pre-lift each `for populated in <ClosedSet>::ALL { let mut spec =
2200    /// ProcessSpec::gate_compute_defaults(); spec.classification.<axis> =
2201    /// populated; … }` loop-body 2-line fixture in `tatara-check.rs`
2202    /// (fifteen sites across `evaluate_point_require_tag` per-axis probes
2203    /// on `point-type-<kind>`, `substrate-<kind>`, `calm-<kind>`,
2204    /// `data-classification-<kind>`, `input-arity-<kind>`,
2205    /// `output-arity-<kind>`, `coordination-required`, `data-regulated`,
2206    /// `data-restricted`, `endomorphic-point`, `diffusive-point`,
2207    /// `convergent-point`, the three-way XOR partition and the two peer
2208    /// implication / mutex fixtures) restated the direct-scalar
2209    /// classification-slot assignment inline; post-lift each site reads
2210    /// `for populated in <ClosedSet>::ALL { let spec =
2211    /// ProcessSpec::gate_compute_with_axis(populated); … }` — one line,
2212    /// one immutable binding, and every per-axis loop dispatches its
2213    /// per-iteration axis mutation through the SAME
2214    /// [`ClassificationAxis::overlay`] trait rather than by directly
2215    /// poking `spec.classification.<axis>`.
2216    ///
2217    /// Sibling to [`Classification::gate_compute_with_axis`] on the
2218    /// (Classification-slice × ProcessSpec-full) composition-depth axis
2219    /// — that primitive owns the (Classification × single-axis-overlay)
2220    /// construction; this primitive owns the (fresh-spec × single-axis-
2221    /// overlay) construction that wraps the sibling call in a fresh
2222    /// [`Self::gate_compute_defaults`] carrier. A future sixth
2223    /// [`ClassificationAxis`] impl (foreshadowed by the six-axis lattice
2224    /// language on the CRD-facing prose and by the shared
2225    /// [`OptimizationDirection`] and [`HorizonKind`] nested-sub-slot
2226    /// impls) picks up every loop-body fixture that binds through this
2227    /// primitive mechanically — no `spec.classification.<new-axis> =
2228    /// value;` restatement per site.
2229    ///
2230    /// Theory anchor: THEORY.md §II.1 invariant 5 (composition preserves
2231    /// proofs — the [`ClassificationAxis::overlay`] trait owns the axis-
2232    /// dispatch proof at ONE site, and this primitive extends that ONE-
2233    /// site guarantee to the (fresh-`gate_compute_defaults`-spec × single-
2234    /// axis-overlay) construction shape). THEORY.md §VI.1 (generation
2235    /// over composition — the 2-line loop-body `let mut spec = …; spec.
2236    /// classification.<axis> = populated;` shape recurred at FIFTEEN
2237    /// hand-authored callsites past the ★★ PRIME-DIRECTIVE ≥ 2
2238    /// duplication trigger inside one workspace binary and is lifted onto
2239    /// ONE substrate owner here).
2240    #[must_use]
2241    pub fn gate_compute_with_axis<A: ClassificationAxis>(axis: A) -> Self {
2242        let mut spec = Self::gate_compute_defaults();
2243        axis.overlay(&mut spec.classification);
2244        spec
2245    }
2246}
2247
2248/// Process status — every field optional until the reconciler writes it.
2249#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema)]
2250#[serde(rename_all = "camelCase")]
2251pub struct ProcessStatus {
2252    /// Hierarchical PID path — e.g., `"seph.1.7"`.
2253    #[serde(default, skip_serializing_if = "Option::is_none")]
2254    pub pid: Option<String>,
2255
2256    /// Parent PID path (mirror of `spec.identity.parent`, resolved at fork).
2257    #[serde(default, skip_serializing_if = "Option::is_none")]
2258    pub parent: Option<String>,
2259
2260    /// Direct children's PID paths.
2261    #[serde(default)]
2262    pub children: Vec<String>,
2263
2264    /// Resolved identity (name + content hash).
2265    #[serde(default, skip_serializing_if = "Option::is_none")]
2266    pub identity: Option<Identity>,
2267
2268    /// Current phase.
2269    #[serde(default)]
2270    pub phase: ProcessPhase,
2271
2272    /// When the process entered the current phase.
2273    #[serde(default, skip_serializing_if = "Option::is_none")]
2274    pub phase_since: Option<DateTime<Utc>>,
2275
2276    /// Three-pillar attestation (written at end of every successful cycle).
2277    #[serde(default, skip_serializing_if = "Option::is_none")]
2278    pub attestation: Option<ProcessAttestation>,
2279
2280    /// FluxCD resources currently owned by this Process.
2281    #[serde(default)]
2282    pub flux_resources: Vec<FluxResourceRef>,
2283
2284    /// Boundary verification state.
2285    #[serde(default)]
2286    pub boundary: BoundaryStatus,
2287
2288    /// Compliance summary at the latest attestation.
2289    #[serde(default)]
2290    pub compliance: ComplianceStatus,
2291
2292    /// Pending signals (delivered, not yet handled).
2293    #[serde(default)]
2294    pub signal_queue: Vec<ProcessSignal>,
2295
2296    /// Standard K8s Conditions.
2297    #[serde(default)]
2298    pub conditions: Vec<ProcessCondition>,
2299
2300    /// Human-readable last status message.
2301    #[serde(default, skip_serializing_if = "Option::is_none")]
2302    pub message: Option<String>,
2303
2304    /// Exit code (only set on Failed / Reaped).
2305    #[serde(default, skip_serializing_if = "Option::is_none")]
2306    pub exit_code: Option<i32>,
2307}
2308
2309impl ProcessStatus {
2310    /// Canonical phase-slot-only [`ProcessStatus`] fixture — a
2311    /// [`ProcessPhase`] pinned at the caller-supplied variant with every
2312    /// other slot parked at its [`Default`] — the workspace-baseline
2313    /// status shape every pool-reconciler phase-decision fixture and
2314    /// every below-controller test that "just wants a Process whose
2315    /// status carries a specific `phase`, nothing else observed" hand-
2316    /// authored as a 3-line `Some(ProcessStatus { phase, ..Default })`
2317    /// struct-literal at scattered pin sites.
2318    ///
2319    /// Pre-lift the 3-line `ProcessStatus { phase: <ProcessPhase::…>,
2320    /// ..Default::default() }` shape recurred at TWO hand-authored
2321    /// sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold,
2322    /// both inside `tatara-pool-reconciler::controller_pool::tests`:
2323    /// * `process_to_member_state_attested_permanent_is_free` — the
2324    ///   Free-arm pin that binds "a Process whose observed phase is
2325    ///   Attested + whose declared `lifetime` is Permanent maps to
2326    ///   `MemberState::Free`".
2327    /// * `process_to_member_state_attested_ephemeral_is_allocated` —
2328    ///   the Allocated-arm pin that binds the peer transition on the
2329    ///   `Lifetime::Ephemeral` corner.
2330    ///
2331    /// Both pin sites walked the SAME 3-line shape stamping
2332    /// `ProcessPhase::Attested`; the composer serves both directly and
2333    /// stays parameterized on `phase` so a future pin on a peer variant
2334    /// (`Running`, `Reconverging`, `Reaped`) rides the same primitive
2335    /// without a new shape opening.
2336    ///
2337    /// Post-lift each callsite reads
2338    /// `p.status = Some(ProcessStatus::at_phase(ProcessPhase::Attested));`
2339    /// and the phase-slot-only status fixture lives at ONE substrate
2340    /// owner. Sibling to [`ProcessSpec::gate_compute_defaults`] on the
2341    /// (spec × status) construction-shape pair: that primitive owns the
2342    /// FULL-spec baseline builder for every downstream `Process::new`
2343    /// consumer; this primitive owns the phase-slot-observation status
2344    /// builder for every downstream `p.status = Some(...)` fixture.
2345    ///
2346    /// A future normalization of the phase-only status shape (a
2347    /// call-time `phase_since` stamp mirroring the phase-transition
2348    /// writer's discipline, a `boundary` slot default overlay pinning
2349    /// the phase to a matching BoundaryStatus corner, a wired-in
2350    /// `identity` fixture for the phase-decision fixtures that today
2351    /// leave the slot at `None`) lands at THIS ONE function and every
2352    /// downstream phase-decision pin inherits the upgrade mechanically.
2353    /// Directly benefits the P5 shigoto Dag refactor (any RecordingJob
2354    /// test fixture stamping "a Process whose observed phase is X" rides
2355    /// the same composer rather than restating the 3-line shape a third
2356    /// time) and the P3 kenshi-runner library lift (any test-Job
2357    /// controller that binds a phase-observation fixture on its owning
2358    /// Process rides through the same composer as the pool-reconciler's
2359    /// two phase-decision pins).
2360    ///
2361    /// Theory anchor: THEORY.md §VI.1 (generation over composition —
2362    /// the 3-line `ProcessStatus { phase, ..Default::default() }`
2363    /// struct-literal recurred at 2 hand-authored sites past the ★★
2364    /// PRIME-DIRECTIVE ≥ 2 duplication trigger inside one workspace
2365    /// crate, and is lifted onto ONE substrate owner here). THEORY.md
2366    /// §II.1 invariant 5 (composition preserves proofs — the pin block
2367    /// binds the primitive at fail-before-pass-after granularity so a
2368    /// regression that drifted the phase slot pass-through, leaked a
2369    /// sibling slot away from `Default`, or hijacked the composer to
2370    /// stamp a static `phase_since` on the `phase` transition surfaces
2371    /// at THESE pins rather than as silent phase-decision skew across
2372    /// the two pool-reconciler callsites).
2373    #[must_use]
2374    pub fn at_phase(phase: ProcessPhase) -> Self {
2375        Self {
2376            phase,
2377            ..Self::default()
2378        }
2379    }
2380}
2381
2382#[cfg(test)]
2383mod tests {
2384    use super::*;
2385    use crate::classification::{
2386        CalmClassification, ConvergencePointType, DataClassification, HorizonKind,
2387        OptimizationDirection, SubstrateType,
2388    };
2389    use crate::intent::NixIntent;
2390
2391    #[test]
2392    fn minimal_spec_serializes() {
2393        let spec = ProcessSpec {
2394            identity: IdentitySpec::default(),
2395            classification: Classification {
2396                point_type: ConvergencePointType::Gate,
2397                substrate: SubstrateType::Observability,
2398                horizon: Default::default(),
2399                calm: Default::default(),
2400                data_classification: Default::default(),
2401            },
2402            intent: Intent {
2403                nix: Some(NixIntent {
2404                    flake_ref: "github:pleme-io/k8s".into(),
2405                    attribute: "obs".into(),
2406                    system: None,
2407                    attic_cache: None,
2408                    extra_args: vec![],
2409                    delegate_to_nix_build: false,
2410                }),
2411                ..Intent::default()
2412            },
2413            boundary: Default::default(),
2414            compliance: Default::default(),
2415            depends_on: vec![],
2416            signals: Default::default(),
2417            lifetime: Default::default(),
2418            routing: None,
2419            encapsulates: None,
2420            suspended: false,
2421        };
2422        let yaml = serde_yaml::to_string(&spec).unwrap();
2423        assert!(yaml.contains("pointType: Gate"));
2424        assert!(yaml.contains("substrate: Observability"));
2425        assert!(yaml.contains("flakeRef: github:pleme-io/k8s"));
2426    }
2427
2428    // ─── Process::coordinates_or_defaults substrate pins ────────────────
2429    //
2430    // Pins the (namespace, name) coordinate-primitive family on the
2431    // (metadata slot × fallback shape) axis. Fail-before-pass-after
2432    // granularity: a regression that flipped either fallback string,
2433    // swapped the return-tuple axis order, or dropped the
2434    // `Option::as_deref` unwrap surfaces here rather than as silent
2435    // drift at every downstream annotation writer / claim-arbiter row
2436    // builder / render owner-metadata seed.
2437
2438    fn empty_spec() -> ProcessSpec {
2439        // Routes through the ONE substrate composer
2440        // `ProcessSpec::gate_compute_defaults` — pre-lift this was the
2441        // 12-line struct-literal restated verbatim at every fixture in
2442        // this pin family, one of EIGHT hand-authored exact-match sites
2443        // past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold across
2444        // four crates.
2445        ProcessSpec::gate_compute_defaults()
2446    }
2447
2448    #[test]
2449    fn default_namespace_constant_is_k8s_canonical_default() {
2450        // Pins the load-bearing convention that this primitive's
2451        // namespace fallback matches K8s's own implicit-namespace
2452        // spelling. A regression that renamed this to "kube-system"
2453        // or any other K8s-reserved name would silently misroute
2454        // every downstream namespaced-Api call on a Process without
2455        // a metadata.namespace.
2456        assert_eq!(Process::DEFAULT_NAMESPACE, "default");
2457    }
2458
2459    #[test]
2460    fn unnamed_placeholder_constant_matches_prior_annotation_writer_fallback() {
2461        // Pins the load-bearing convention that this primitive's name
2462        // fallback matches the exact spelling every annotation writer
2463        // (tatara-reconciler::ssapply::inject_annotations,
2464        // tatara-reconciler::render::render, and
2465        // tatara-reconciler::table_controller's claim-row builder)
2466        // was hand-authoring pre-lift ("unnamed", NOT "<unnamed>" or
2467        // ""). A regression that renamed this would break the
2468        // annotation-writer / claim-arbiter grep contract silently.
2469        assert_eq!(Process::UNNAMED_PLACEHOLDER, "unnamed");
2470    }
2471
2472    #[test]
2473    fn namespace_or_default_falls_back_when_metadata_namespace_is_none() {
2474        let mut p = Process::new("some-proc", empty_spec());
2475        p.metadata.namespace = None;
2476        assert_eq!(p.namespace_or_default(), Process::DEFAULT_NAMESPACE);
2477    }
2478
2479    #[test]
2480    fn namespace_or_default_returns_metadata_slice_when_some() {
2481        let mut p = Process::new("some-proc", empty_spec());
2482        p.metadata.namespace = Some("prod-app".into());
2483        assert_eq!(p.namespace_or_default(), "prod-app");
2484    }
2485
2486    #[test]
2487    fn name_or_placeholder_falls_back_when_metadata_name_is_none() {
2488        let mut p = Process::new("real-name", empty_spec());
2489        p.metadata.name = None;
2490        assert_eq!(p.name_or_placeholder(), Process::UNNAMED_PLACEHOLDER);
2491    }
2492
2493    #[test]
2494    fn name_or_placeholder_returns_metadata_slice_when_some() {
2495        let p = Process::new("api-gateway", empty_spec());
2496        assert_eq!(p.name_or_placeholder(), "api-gateway");
2497    }
2498
2499    #[test]
2500    fn coordinates_or_defaults_composes_both_halves() {
2501        // Both slots present — returns metadata slices in
2502        // (namespace, name) axis order.
2503        let mut p = Process::new("api", empty_spec());
2504        p.metadata.namespace = Some("staging".into());
2505        assert_eq!(p.coordinates_or_defaults(), ("staging", "api"));
2506    }
2507
2508    #[test]
2509    fn coordinates_or_defaults_falls_back_on_both_slots() {
2510        // Both slots None — returns (DEFAULT_NAMESPACE,
2511        // UNNAMED_PLACEHOLDER) in axis order.
2512        let mut p = Process::new("scratch", empty_spec());
2513        p.metadata.name = None;
2514        p.metadata.namespace = None;
2515        assert_eq!(
2516            p.coordinates_or_defaults(),
2517            (Process::DEFAULT_NAMESPACE, Process::UNNAMED_PLACEHOLDER)
2518        );
2519    }
2520
2521    #[test]
2522    fn coordinates_or_defaults_mixes_slotted_and_fallback_halves() {
2523        // Namespace set, name missing — the (namespace, name) tuple
2524        // pins each half independently. A regression that returned
2525        // BOTH fallbacks when EITHER metadata slot was None would
2526        // surface here rather than at every downstream reader.
2527        let mut p = Process::new("kept-name", empty_spec());
2528        p.metadata.namespace = Some("prod".into());
2529        assert_eq!(p.coordinates_or_defaults(), ("prod", "kept-name"));
2530
2531        // Name set, namespace missing — the peer corner.
2532        let mut q = Process::new("api", empty_spec());
2533        q.metadata.namespace = None;
2534        assert_eq!(
2535            q.coordinates_or_defaults(),
2536            (Process::DEFAULT_NAMESPACE, "api")
2537        );
2538    }
2539
2540    // ─── Process::qualified_ref substrate pins ─────────────────────────
2541    //
2542    // Pins the paired-projection + shape-composer chain
2543    // `coordinates_or_defaults() → qualified_process_ref(ns, name)` on
2544    // the (return-form × composition-depth) axis pair. Fail-before-
2545    // pass-after granularity: a regression that swapped the `<ns>/<name>`
2546    // axis order, dropped either half, drifted the fallback strings
2547    // between the paired-projection primitive and the shape composer, or
2548    // inserted a normalization step at only the composed site and not
2549    // the pair-returning primitive (or vice versa) surfaces here rather
2550    // than as silent operator-visible skew across the three pre-lift
2551    // `tatara-reconciler` sites (`render::render_routing`,
2552    // `render::render_export_jobs`, `table_controller::reconcile`)
2553    // whose downstream greps the reference shape verbatim (the
2554    // `PROCESS=<ref>` annotation seed on every emitted Ingress /
2555    // DNSEndpoint / export Job, the `ClaimRecord.holder` slot on the
2556    // stable-name claim registry).
2557
2558    #[test]
2559    fn qualified_ref_composes_ns_and_name_with_slash_when_both_slots_present() {
2560        // Happy path — both metadata slots populated. The composed
2561        // reference is EXACTLY `<ns>/<name>`, in that order, joined by
2562        // a single `/`. A regression that swapped the two axes at
2563        // this primitive would silently break every downstream
2564        // `PROCESS=<ref>` annotation grep + claim-registry lookup.
2565        let mut p = Process::new("api-gateway", empty_spec());
2566        p.metadata.namespace = Some("prod-app".into());
2567        assert_eq!(p.qualified_ref(), "prod-app/api-gateway");
2568    }
2569
2570    #[test]
2571    fn qualified_ref_falls_back_to_default_namespace_when_metadata_namespace_is_none() {
2572        // Namespace-fallback pin: an absent `metadata.namespace` rides
2573        // through `namespace_or_default()` → `DEFAULT_NAMESPACE`, so
2574        // the composed reference lands as `default/<name>`. Matches
2575        // what a pre-lift `qualified_process_ref(process.
2576        // coordinates_or_defaults())` composition produced.
2577        let mut p = Process::new("api-gateway", empty_spec());
2578        p.metadata.namespace = None;
2579        assert_eq!(p.qualified_ref(), "default/api-gateway");
2580    }
2581
2582    #[test]
2583    fn qualified_ref_falls_back_to_unnamed_placeholder_when_metadata_name_is_none() {
2584        // Name-fallback pin: an absent `metadata.name` rides through
2585        // `name_or_placeholder()` → `UNNAMED_PLACEHOLDER`, so the
2586        // composed reference lands as `<ns>/unnamed`. A pre-lift
2587        // consumer whose paired projection returned the placeholder
2588        // (annotation writer, render owner-metadata seed) sees the
2589        // exact same `<ns>/unnamed` shape post-lift, so downstream
2590        // greps keyed on the pre-metadata Process's reference match
2591        // bytewise.
2592        let mut p = Process::new("ignored", empty_spec());
2593        p.metadata.namespace = Some("staging".into());
2594        p.metadata.name = None;
2595        assert_eq!(p.qualified_ref(), "staging/unnamed");
2596    }
2597
2598    #[test]
2599    fn qualified_ref_falls_back_on_both_slots_when_both_metadata_are_none() {
2600        // Both slots absent → both fallbacks land in the composed
2601        // reference. The `default/unnamed` shape is what every pre-
2602        // lift caller produced when a Process fixture (test or
2603        // dynamic API response) surfaced without populated metadata;
2604        // pinning it here holds the primitive's contract against a
2605        // regression that dropped either fallback at only the
2606        // composed site.
2607        let mut p = Process::new("ignored", empty_spec());
2608        p.metadata.namespace = None;
2609        p.metadata.name = None;
2610        assert_eq!(
2611            p.qualified_ref(),
2612            format!(
2613                "{}/{}",
2614                Process::DEFAULT_NAMESPACE,
2615                Process::UNNAMED_PLACEHOLDER
2616            )
2617        );
2618    }
2619
2620    #[test]
2621    fn qualified_ref_matches_pre_lift_paired_composition_bytewise() {
2622        // Byte-identical parity with the exact pre-lift 2-step
2623        // composition every `tatara-reconciler` site hand-authored:
2624        // `let (ns, name) = process.coordinates_or_defaults(); let r
2625        // = qualified_process_ref(ns, name);`. Sweeps every metadata-
2626        // slot combination the three pre-lift consumers plausibly
2627        // encountered — both slots populated (steady state), one
2628        // slot absent (Process mid-fork before API-server metadata
2629        // stamp), both slots absent (dynamic API response / test
2630        // fixture) — so a regression that reshaped the composition at
2631        // the substrate primitive would surface here rather than as
2632        // silent drift at the three consumer sites.
2633        let fixtures: [(Option<&str>, Option<&str>); 4] = [
2634            (Some("prod-app"), Some("api-gateway")),
2635            (None, Some("api-gateway")),
2636            (Some("staging"), None),
2637            (None, None),
2638        ];
2639        for (ns_slot, name_slot) in fixtures {
2640            let mut p = Process::new(name_slot.unwrap_or("seed"), empty_spec());
2641            p.metadata.namespace = ns_slot.map(str::to_string);
2642            p.metadata.name = name_slot.map(str::to_string);
2643            let via_primitive = p.qualified_ref();
2644            let (ns, name) = p.coordinates_or_defaults();
2645            let via_paired = crate::qualified_process_ref(ns, name);
2646            assert_eq!(
2647                via_primitive, via_paired,
2648                "qualified_ref must be byte-identical to the pre-lift \
2649                 paired composition on (ns={ns_slot:?}, name={name_slot:?})"
2650            );
2651        }
2652    }
2653
2654    #[test]
2655    fn qualified_ref_composes_from_the_shared_coordinates_or_defaults_owner() {
2656        // Composition invariant: the composed reference decomposes at
2657        // the single `/` separator into EXACTLY the (ns, name) pair
2658        // `coordinates_or_defaults` returns. A regression that
2659        // introduced a per-callsite normalization at the shape
2660        // composer (URL-escape, case-fold, path-normalize) or that
2661        // pulled the pair from a different metadata source than the
2662        // paired-projection primitive would surface here rather than
2663        // at every downstream reference-shape grep.
2664        let mut p = Process::new("api-gateway", empty_spec());
2665        p.metadata.namespace = Some("prod-app".into());
2666        let composed = p.qualified_ref();
2667        let (ns, name) = p.coordinates_or_defaults();
2668        let (composed_ns, composed_name) = composed.split_once('/').unwrap();
2669        assert_eq!(composed_ns, ns);
2670        assert_eq!(composed_name, name);
2671    }
2672
2673    // ─── Process::owned_coordinates_or_err substrate pins ──────────────
2674    //
2675    // Pins the owned + name-required peer of the coordinate-primitive
2676    // family on the (return-form × name gate) axis pair. Fail-before-
2677    // pass-after granularity: a regression that flipped the namespace
2678    // fallback string, dropped the `Option::clone` unwrap, changed the
2679    // return-tuple axis order, or altered the "Process has no
2680    // metadata.name" error wording surfaces here rather than as silent
2681    // drift at every pre-lift caller (10 sites in
2682    // `tatara-reconciler::phase_machine` + 2 sites in
2683    // `tatara-reconciler::signals` pre-lift).
2684
2685    #[test]
2686    fn owned_coordinates_or_err_returns_owned_strings_when_both_slots_present() {
2687        // Happy path — both slots populated, method returns owned
2688        // Strings in (namespace, name) axis order.
2689        let mut p = Process::new("api-gateway", empty_spec());
2690        p.metadata.namespace = Some("prod-app".into());
2691        let (ns, name) = p.owned_coordinates_or_err().unwrap();
2692        assert_eq!(ns, "prod-app");
2693        assert_eq!(name, "api-gateway");
2694        // Ownership pin: type inference above binds ns/name as
2695        // owned Strings — a regression that returned &str would
2696        // fail to compile at the following .push() call. This
2697        // holds the "owned" half of the primitive's contract.
2698        let mut owned_ns = ns;
2699        owned_ns.push_str("-mutated");
2700        assert_eq!(owned_ns, "prod-app-mutated");
2701    }
2702
2703    #[test]
2704    fn owned_coordinates_or_err_falls_back_on_namespace_but_returns_owned_name() {
2705        // Namespace absent → DEFAULT_NAMESPACE. Name present → owned.
2706        let p = Process::new("api", empty_spec());
2707        // Process::new leaves metadata.namespace = None by default.
2708        let (ns, name) = p.owned_coordinates_or_err().unwrap();
2709        assert_eq!(ns, Process::DEFAULT_NAMESPACE);
2710        assert_eq!(name, "api");
2711    }
2712
2713    #[test]
2714    fn owned_coordinates_or_err_errors_when_metadata_name_absent_regardless_of_namespace() {
2715        // Name absent → Err, REGARDLESS of whether the namespace is
2716        // populated. The name gate is strictly on `metadata.name` and
2717        // does NOT fall back to `Self::UNNAMED_PLACEHOLDER` (that
2718        // fallback is on the peer `coordinates_or_defaults`, which
2719        // exists precisely for consumers that can tolerate a
2720        // display placeholder).
2721        for ns_slot in [None, Some("prod".to_string())] {
2722            let mut p = Process::new("scratch", empty_spec());
2723            p.metadata.name = None;
2724            p.metadata.namespace = ns_slot.clone();
2725            let err = p.owned_coordinates_or_err().unwrap_err();
2726            assert!(
2727                err.to_string().contains("metadata.name"),
2728                "err on missing name (ns={ns_slot:?}) should mention metadata.name; got {err}"
2729            );
2730        }
2731    }
2732
2733    #[test]
2734    fn owned_coordinates_or_err_error_message_matches_pre_lift_reconciler_wording() {
2735        // Load-bearing wording pin — every pre-lift `tatara-reconciler`
2736        // helper (`phase_machine::namespace_and_name`,
2737        // `signals::ingest`, `signals::consume_effect`) errored with
2738        // EXACTLY this wording. Post-lift the substrate owner produces
2739        // the same wording so log-line / test greps that anchored on
2740        // it keep matching, and no operator-visible message drift
2741        // lands as a side effect of the substrate move.
2742        let mut p = Process::new("scratch", empty_spec());
2743        p.metadata.name = None;
2744        let err = p.owned_coordinates_or_err().unwrap_err();
2745        assert_eq!(err.to_string(), "Process has no metadata.name");
2746    }
2747
2748    #[test]
2749    fn owned_coordinates_or_err_namespace_fallback_matches_default_namespace_const() {
2750        // Byte-identity pin between the owned form's namespace
2751        // fallback and the workspace-wide `DEFAULT_NAMESPACE` const.
2752        // A regression that spelled this fallback as any other
2753        // string ("kube-system", "", "default-ns") would silently
2754        // misroute every downstream namespaced-Api call on a
2755        // Process without a metadata.namespace — surfaces here
2756        // rather than at every kube-rs API caller.
2757        let mut p = Process::new("api", empty_spec());
2758        p.metadata.namespace = None;
2759        let (ns, _) = p.owned_coordinates_or_err().unwrap();
2760        assert_eq!(ns, Process::DEFAULT_NAMESPACE);
2761    }
2762
2763    #[test]
2764    fn owned_coordinates_or_err_matches_pre_lift_reconciler_helper_shape() {
2765        // Byte-identical parity pin between the owned + name-required
2766        // primitive here and the pre-lift `tatara-reconciler` helper
2767        // shape — the exact 2-slot unwrap chain each pre-lift caller
2768        // spelled by hand:
2769        //
2770        //   let ns = p.metadata.namespace.clone().unwrap_or_else(|| "default".into());
2771        //   let name = p.metadata.name.clone().ok_or_else(|| anyhow!(...))?;
2772        //   Ok((ns, name))
2773        //
2774        // Sweeps every corner every callsite plausibly encounters
2775        // (both slots present, namespace absent, name absent, both
2776        // absent). A regression that inserted a normalization step
2777        // at the primitive that the pre-lift chain does NOT apply —
2778        // or vice versa — surfaces here rather than as silent drift
2779        // between the 12 pre-lift consumer callsites and the ONE
2780        // substrate owner they now route through.
2781        fn pre_lift(p: &Process) -> anyhow::Result<(String, String)> {
2782            let ns = p
2783                .metadata
2784                .namespace
2785                .clone()
2786                .unwrap_or_else(|| "default".into());
2787            let name = p
2788                .metadata
2789                .name
2790                .clone()
2791                .ok_or_else(|| anyhow::anyhow!("Process has no metadata.name"))?;
2792            Ok((ns, name))
2793        }
2794        // Both present.
2795        let mut p = Process::new("api", empty_spec());
2796        p.metadata.namespace = Some("prod".into());
2797        assert_eq!(p.owned_coordinates_or_err().unwrap(), pre_lift(&p).unwrap());
2798        // Namespace absent.
2799        let p = Process::new("api", empty_spec());
2800        assert_eq!(p.owned_coordinates_or_err().unwrap(), pre_lift(&p).unwrap());
2801        // Name absent → both variants error with the same wording.
2802        let mut p = Process::new("api", empty_spec());
2803        p.metadata.name = None;
2804        p.metadata.namespace = Some("prod".into());
2805        assert_eq!(
2806            p.owned_coordinates_or_err().unwrap_err().to_string(),
2807            pre_lift(&p).unwrap_err().to_string(),
2808        );
2809        // Both absent → still errors on the name gate.
2810        let mut p = Process::new("api", empty_spec());
2811        p.metadata.name = None;
2812        p.metadata.namespace = None;
2813        assert_eq!(
2814            p.owned_coordinates_or_err().unwrap_err().to_string(),
2815            pre_lift(&p).unwrap_err().to_string(),
2816        );
2817    }
2818
2819    #[test]
2820    fn owned_coordinates_or_err_axis_order_matches_coordinates_or_defaults() {
2821        // Cross-primitive coherence pin between the owned + name-
2822        // required form and the borrow + name-defaulted peer:
2823        // (namespace, name) axis order is IDENTICAL across both
2824        // return-forms. A regression that swapped the tuple slots on
2825        // only ONE of the two primitives would silently misroute
2826        // every consumer that picked between the two forms based on
2827        // its callsite's ownership needs. The pin re-reads both
2828        // primitives at test time so the equality holds iff both
2829        // live paths are the current implementation.
2830        let mut p = Process::new("app", empty_spec());
2831        p.metadata.namespace = Some("infra".into());
2832        let (borrow_ns, borrow_name) = p.coordinates_or_defaults();
2833        let (owned_ns, owned_name) = p.owned_coordinates_or_err().unwrap();
2834        assert_eq!(owned_ns, borrow_ns);
2835        assert_eq!(owned_name, borrow_name);
2836        // Explicit slot labels — pins the (namespace, name) axis
2837        // order as opposed to (name, namespace).
2838        assert_eq!(owned_ns, "infra"); // NOT "app"
2839        assert_eq!(owned_name, "app"); // NOT "infra"
2840    }
2841
2842    // ─── Process::coordinates_or_none substrate pins ──────────────────
2843    //
2844    // Pins the borrow + name-required peer of the coordinate-primitive
2845    // family on the (return-form × name-gate) axis pair. Closes the
2846    // corner previously left open (borrow + name-required) so the
2847    // three consumer shapes (child-Process delete-fan-out at
2848    // `phase_machine::handle_exiting`, claim-arbiter probe at
2849    // `phase_machine::process_holds_any_claim`, any future non-fatal
2850    // skip site) route through ONE primitive rather than three hand-
2851    // authored empty-string / `unwrap_or_default()` sentinel chains.
2852    // Fail-before-pass-after granularity: a regression that flipped
2853    // the namespace fallback, swapped the return-tuple axis order,
2854    // returned an owned form, or promoted a missing name to an error
2855    // rather than `None` surfaces here rather than as silent drift at
2856    // every borrow + name-required consumer.
2857
2858    #[test]
2859    fn coordinates_or_none_returns_slices_when_both_slots_present() {
2860        // Happy path — both slots populated, method returns borrowed
2861        // (&str, &str) in (namespace, name) axis order wrapped in
2862        // `Some`.
2863        let mut p = Process::new("api-gateway", empty_spec());
2864        p.metadata.namespace = Some("prod-app".into());
2865        let (ns, name) = p.coordinates_or_none().expect("Some when name set");
2866        assert_eq!(ns, "prod-app");
2867        assert_eq!(name, "api-gateway");
2868    }
2869
2870    #[test]
2871    fn coordinates_or_none_falls_back_on_namespace_but_returns_name_slice() {
2872        // Namespace absent → DEFAULT_NAMESPACE (shared with the peer
2873        // `coordinates_or_defaults` + `namespace_or_default`). Name
2874        // present → the metadata slice, wrapped in `Some`.
2875        let mut p = Process::new("api", empty_spec());
2876        p.metadata.namespace = None;
2877        let (ns, name) = p.coordinates_or_none().expect("Some when name set");
2878        assert_eq!(ns, Process::DEFAULT_NAMESPACE);
2879        assert_eq!(name, "api");
2880    }
2881
2882    #[test]
2883    fn coordinates_or_none_returns_none_when_metadata_name_absent_regardless_of_namespace() {
2884        // Name absent → `None`, REGARDLESS of whether the namespace
2885        // slot is populated. The name gate is strictly on
2886        // `metadata.name` and does NOT fall back to
2887        // `Self::UNNAMED_PLACEHOLDER` (that fallback is on the peer
2888        // `coordinates_or_defaults`, which exists precisely for
2889        // consumers that tolerate a display placeholder). Peer to
2890        // `owned_coordinates_or_err_errors_when_metadata_name_absent_regardless_of_namespace`
2891        // on the sibling primitive; a regression that widened THIS
2892        // form to substitute the placeholder while leaving the owned
2893        // form strict would silently drift the two borrow-form
2894        // primitives out of the coherence the family carries.
2895        for ns_slot in [None, Some("prod".to_string())] {
2896            let mut p = Process::new("scratch", empty_spec());
2897            p.metadata.name = None;
2898            p.metadata.namespace = ns_slot.clone();
2899            assert!(
2900                p.coordinates_or_none().is_none(),
2901                "coordinates_or_none must be None on missing name (ns={ns_slot:?})",
2902            );
2903        }
2904    }
2905
2906    #[test]
2907    fn coordinates_or_none_namespace_fallback_matches_default_namespace_const() {
2908        // Byte-identity pin between the borrow + name-required form's
2909        // namespace fallback and the workspace-wide `DEFAULT_NAMESPACE`
2910        // const. Sibling to
2911        // `owned_coordinates_or_err_namespace_fallback_matches_default_namespace_const`
2912        // on the peer primitive — the two forms MUST substitute the
2913        // same fallback string, else a consumer that switches between
2914        // them based on its ownership need silently observes a
2915        // different namespace-fallback shape as a side effect.
2916        let mut p = Process::new("api", empty_spec());
2917        p.metadata.namespace = None;
2918        let (ns, _) = p.coordinates_or_none().unwrap();
2919        assert_eq!(ns, Process::DEFAULT_NAMESPACE);
2920    }
2921
2922    #[test]
2923    fn coordinates_or_none_axis_order_matches_coordinates_or_defaults_when_name_present() {
2924        // Cross-primitive coherence pin between the two borrow-form
2925        // primitives: when the name is present, the (namespace, name)
2926        // return-tuple axis order is IDENTICAL across the two forms,
2927        // and the returned slices are the SAME `&str` view onto the
2928        // same metadata slots. A regression that swapped the tuple
2929        // slots on ONE form would silently misroute every consumer
2930        // that picked between the two forms based on its name-gate
2931        // need. The pin re-reads both primitives at test time so the
2932        // equality holds iff both live paths are the current
2933        // implementation.
2934        let mut p = Process::new("app", empty_spec());
2935        p.metadata.namespace = Some("infra".into());
2936        let (defaulted_ns, defaulted_name) = p.coordinates_or_defaults();
2937        let (required_ns, required_name) = p.coordinates_or_none().unwrap();
2938        assert_eq!(defaulted_ns, required_ns);
2939        assert_eq!(defaulted_name, required_name);
2940        // Explicit slot labels — pins the (namespace, name) axis order
2941        // as opposed to (name, namespace).
2942        assert_eq!(required_ns, "infra"); // NOT "app"
2943        assert_eq!(required_name, "app"); // NOT "infra"
2944    }
2945
2946    #[test]
2947    fn coordinates_or_none_axis_pair_diverges_from_coordinates_or_defaults_on_missing_name() {
2948        // Divergence pin between the two borrow-form primitives when
2949        // the name gate fires: `coordinates_or_defaults` substitutes
2950        // the display placeholder AND still returns a tuple;
2951        // `coordinates_or_none` returns `None`. A regression that
2952        // collapsed the two behaviors (either by dropping the gate
2953        // from the required form or by adding a `None` corner to the
2954        // defaulted form) would blur the axis pair's whole reason to
2955        // exist as two peer primitives.
2956        let mut p = Process::new("scratch", empty_spec());
2957        p.metadata.name = None;
2958        p.metadata.namespace = Some("prod".into());
2959        // Defaulted form: substitutes placeholder, no gate.
2960        assert_eq!(
2961            p.coordinates_or_defaults(),
2962            ("prod", Process::UNNAMED_PLACEHOLDER)
2963        );
2964        // Required form: gate fires, `None`.
2965        assert!(p.coordinates_or_none().is_none());
2966    }
2967
2968    #[test]
2969    fn coordinates_or_none_matches_pre_lift_reconciler_helper_shape() {
2970        // Byte-identical parity pin between the borrow + name-required
2971        // primitive here and the pre-lift `tatara-reconciler` helper
2972        // shapes — the exact 2-slot unwrap + gate chains each pre-lift
2973        // caller spelled by hand (`phase_machine::process_holds_any_claim`
2974        // spelled it as `unwrap_or("")` + `is_empty` early-return;
2975        // `phase_machine::handle_exiting`'s child-fan-out spelled it
2976        // as `unwrap_or_default()` + implicit no-op delete on the
2977        // empty API-path). Sweeps every corner every callsite plausibly
2978        // encounters (both slots present, namespace absent, name
2979        // absent + ns present, both absent). A regression that
2980        // inserted a normalization step at the primitive the pre-lift
2981        // chain does NOT apply — or vice versa — surfaces here rather
2982        // than as silent drift between the pre-lift consumer sites
2983        // and the ONE substrate owner they now route through.
2984        fn pre_lift_holds_any_claim(p: &Process) -> Option<(&str, &str)> {
2985            let ns = p.metadata.namespace.as_deref().unwrap_or("default");
2986            let name = p.metadata.name.as_deref().unwrap_or("");
2987            if name.is_empty() {
2988                return None;
2989            }
2990            Some((ns, name))
2991        }
2992        // Both present.
2993        let mut p = Process::new("api", empty_spec());
2994        p.metadata.namespace = Some("prod".into());
2995        assert_eq!(p.coordinates_or_none(), pre_lift_holds_any_claim(&p));
2996        // Namespace absent.
2997        let p = Process::new("api", empty_spec());
2998        assert_eq!(p.coordinates_or_none(), pre_lift_holds_any_claim(&p));
2999        // Name absent → both variants return `None` regardless of ns.
3000        let mut p = Process::new("api", empty_spec());
3001        p.metadata.name = None;
3002        p.metadata.namespace = Some("prod".into());
3003        assert_eq!(p.coordinates_or_none(), pre_lift_holds_any_claim(&p));
3004        // Both absent → still `None` on the name gate.
3005        let mut p = Process::new("api", empty_spec());
3006        p.metadata.name = None;
3007        p.metadata.namespace = None;
3008        assert_eq!(p.coordinates_or_none(), pre_lift_holds_any_claim(&p));
3009    }
3010
3011    #[test]
3012    fn coordinates_or_none_axis_order_matches_owned_coordinates_or_err_on_happy_path() {
3013        // Cross-primitive coherence pin at the sibling corner: when
3014        // BOTH slots are present, the borrow + name-required form
3015        // (this method) and the owned + name-required peer
3016        // (`owned_coordinates_or_err`) return the SAME `(ns, name)`
3017        // pair — the axis order is IDENTICAL and neither primitive
3018        // silently applies a normalization the other omits. A
3019        // regression that skewed one form's normalization would
3020        // surface here rather than as silent drift between the two
3021        // name-required corners of the primitive family.
3022        let mut p = Process::new("app", empty_spec());
3023        p.metadata.namespace = Some("infra".into());
3024        let (borrow_ns, borrow_name) = p.coordinates_or_none().unwrap();
3025        let (owned_ns, owned_name) = p.owned_coordinates_or_err().unwrap();
3026        assert_eq!(borrow_ns, owned_ns.as_str());
3027        assert_eq!(borrow_name, owned_name.as_str());
3028    }
3029
3030    #[test]
3031    fn coordinates_or_defaults_axis_order_matches_qualified_process_ref() {
3032        // Pins the load-bearing convention that the return-tuple
3033        // axis order is (namespace, name) — the exact positional
3034        // argument order the substrate's paired-composer primitive
3035        // `tatara_reconciler::ssapply::qualified_process_ref(ns,
3036        // name)` consumes. A regression that swapped the tuple
3037        // slots would silently misroute every annotation writer /
3038        // claim-arbiter row / owner-metadata seed built by feeding
3039        // this pair into the composer — every downstream `<ns>/
3040        // <name>` grep would suddenly see `<name>/<ns>`. The test
3041        // verifies the tuple's first slot is what a hand-authored
3042        // `.metadata.namespace.as_deref()...` produced pre-lift, and
3043        // the second slot is what `.metadata.name.as_deref()...`
3044        // produced.
3045        let mut p = Process::new("app", empty_spec());
3046        p.metadata.namespace = Some("infra".into());
3047        let (ns, name) = p.coordinates_or_defaults();
3048        assert_eq!(ns, "infra"); // NOT "app"
3049        assert_eq!(name, "app"); // NOT "infra"
3050    }
3051
3052    // ─── Process::annotation substrate pins ────────────────────────────
3053    //
3054    // Pins the borrow-form annotation-lookup primitive that owns the
3055    // 3-line `.metadata.annotations.as_ref().and_then(|m| m.get(KEY))`
3056    // chain three hand-authored sites restated by hand pre-lift:
3057    // `tatara-reconciler::signals::ingest` (SIGNAL),
3058    // `tatara-reconciler::phase_machine::released_from_annotation`
3059    // (RELEASED_FROM), and
3060    // `tatara-pool-reconciler::controller_pool::process_belongs_to_pool`
3061    // (POOL). Fail-before-pass-after granularity: a regression that
3062    // widened the missing-`annotations` corner (returning `Some("")`
3063    // instead of `None`), promoted a missing key to an error, dropped
3064    // the borrow-form return, or changed the two swallowed corners'
3065    // shared collapse to `None` surfaces here rather than as silent
3066    // drift at the three consumer sites.
3067    fn process_with_annotation(key: &str, value: &str) -> Process {
3068        let mut p = Process::new("some-proc", empty_spec());
3069        let mut anns = std::collections::BTreeMap::new();
3070        anns.insert(key.to_string(), value.to_string());
3071        p.metadata.annotations = Some(anns);
3072        p
3073    }
3074
3075    #[test]
3076    fn annotation_returns_none_when_metadata_annotations_is_none() {
3077        // Missing-`annotations` corner: a Process with no annotations
3078        // block at all returns `None` for every key. Peer to
3079        // `observed_flux_resources_returns_empty_slice_when_status_is_none`
3080        // on the status-projection axis; both primitives collapse the
3081        // outer `Option` corner rather than requiring each consumer
3082        // to spell the guard by hand.
3083        let mut p = Process::new("scratch", empty_spec());
3084        p.metadata.annotations = None;
3085        assert!(p.annotation("tatara.pleme.io/signal").is_none());
3086        assert!(p.annotation("tatara.pleme.io/pool").is_none());
3087        assert!(p.annotation("").is_none());
3088    }
3089
3090    #[test]
3091    fn annotation_returns_none_when_key_absent_from_populated_map() {
3092        // Missing-key corner: annotations block populated with OTHER
3093        // keys returns `None` for the queried key. Symmetric with the
3094        // missing-`annotations` corner — both corners collapse to the
3095        // same `None`, matching the pre-lift `.and_then(...)`
3096        // behavior every consumer relied on.
3097        let p = process_with_annotation("tatara.pleme.io/other", "value");
3098        assert!(p.annotation("tatara.pleme.io/signal").is_none());
3099        assert!(p.annotation("").is_none());
3100    }
3101
3102    #[test]
3103    fn annotation_returns_borrowed_slice_when_key_present() {
3104        // Happy path: annotations block populated + key present →
3105        // `Some(&str)` borrowed from the underlying `String` in the
3106        // map. A regression that returned an owned `String` (defeating
3107        // the primitive's role as a zero-copy projection) would
3108        // surface at the lifetime of the returned reference — the
3109        // `&str` outlives the borrow of `&p` here.
3110        let p = process_with_annotation("tatara.pleme.io/signal", "SIGHUP");
3111        assert_eq!(p.annotation("tatara.pleme.io/signal"), Some("SIGHUP"));
3112    }
3113
3114    #[test]
3115    fn annotation_returns_borrowed_empty_string_slice_when_value_is_empty() {
3116        // Edge corner between the missing-key `None` and the present-
3117        // key `Some("")` — a Process whose annotation is EXPLICITLY
3118        // set to an empty string returns `Some("")`, NOT `None`. A
3119        // regression that normalized the empty-string value to `None`
3120        // (a plausible "defensive" simplification) would silently
3121        // reshape the corner every callsite pre-lift kept distinct via
3122        // `.cloned().unwrap_or_default()` (which collapses BOTH to
3123        // `""`) or `.map(String::as_str)` (which keeps them distinct
3124        // as `None` vs `Some("")`).
3125        let p = process_with_annotation("tatara.pleme.io/signal", "");
3126        assert_eq!(p.annotation("tatara.pleme.io/signal"), Some(""));
3127    }
3128
3129    #[test]
3130    fn annotation_is_a_pure_projection() {
3131        // Purity pin — repeated calls return equal results and the
3132        // primitive does not mutate `self`. Peer to
3133        // `observed_flux_resources_is_a_pure_projection` on the
3134        // status-projection axis.
3135        let p = process_with_annotation("tatara.pleme.io/released-from", "Attested");
3136        let a = p.annotation("tatara.pleme.io/released-from");
3137        let b = p.annotation("tatara.pleme.io/released-from");
3138        assert_eq!(a, b);
3139        assert_eq!(a, Some("Attested"));
3140    }
3141
3142    #[test]
3143    fn annotation_matches_pre_lift_reconciler_chain_shape() {
3144        // Byte-identical parity pin between the borrow-form primitive
3145        // here and the pre-lift `tatara-reconciler` / `tatara-pool-
3146        // reconciler` chain shape — the exact 3-line
3147        // `.metadata.annotations.as_ref().and_then(|m| m.get(KEY))
3148        // .map(String::as_str)` incantation each pre-lift caller
3149        // spelled by hand (three variants of tail collapsed onto ONE
3150        // borrow-form primitive here; each caller reapplies its own
3151        // tail at its own site). Sweeps every corner (missing
3152        // annotations map, missing key, present key with value,
3153        // present key with empty value) so a regression that inserted
3154        // a normalization at the primitive the pre-lift chain does
3155        // NOT apply — or vice versa — surfaces here rather than as
3156        // silent drift between the ONE substrate owner and the three
3157        // consumer sites.
3158        fn pre_lift<'a>(p: &'a Process, key: &str) -> Option<&'a str> {
3159            p.metadata
3160                .annotations
3161                .as_ref()
3162                .and_then(|m| m.get(key))
3163                .map(String::as_str)
3164        }
3165        // Missing annotations map.
3166        let mut p = Process::new("x", empty_spec());
3167        p.metadata.annotations = None;
3168        assert_eq!(p.annotation("k"), pre_lift(&p, "k"));
3169        // Missing key in populated map.
3170        let p = process_with_annotation("other", "v");
3171        assert_eq!(p.annotation("k"), pre_lift(&p, "k"));
3172        // Present key with non-empty value.
3173        let p = process_with_annotation("k", "v");
3174        assert_eq!(p.annotation("k"), pre_lift(&p, "k"));
3175        // Present key with explicitly-empty value — the corner
3176        // `.cloned().unwrap_or_default()` collapses to `""` post-tail
3177        // but the primitive-level shape stays `Some("")`.
3178        let p = process_with_annotation("k", "");
3179        assert_eq!(p.annotation("k"), pre_lift(&p, "k"));
3180    }
3181
3182    #[test]
3183    fn annotation_composes_owned_tail_matching_pre_lift_signals_ingest() {
3184        // Pins the exact tail shape `tatara-reconciler::signals::
3185        // ingest` composed pre-lift: an `Option<String>` for the
3186        // downstream `let Some(raw) = raw else { ... }` guard.
3187        // Post-lift the callsite composes `.map(str::to_string)` at
3188        // its own site; this test pins the composition matches the
3189        // pre-lift `.cloned()` tail byte-for-byte on both corners the
3190        // consumer's downstream distinguishes (annotation present →
3191        // `Some(String)`; absent → `None`).
3192        let p = process_with_annotation("tatara.pleme.io/signal", "SIGUSR1");
3193        assert_eq!(
3194            p.annotation("tatara.pleme.io/signal").map(str::to_string),
3195            Some("SIGUSR1".to_string())
3196        );
3197        let mut q = Process::new("y", empty_spec());
3198        q.metadata.annotations = None;
3199        assert_eq!(
3200            q.annotation("tatara.pleme.io/signal").map(str::to_string),
3201            None
3202        );
3203    }
3204
3205    #[test]
3206    fn annotation_composes_default_tail_matching_pre_lift_released_from() {
3207        // Pins the exact tail shape
3208        // `tatara-reconciler::phase_machine::released_from_annotation`
3209        // composed pre-lift: a bare `String` via `.cloned()
3210        // .unwrap_or_default()` for the downstream
3211        // `match v.as_str()` dispatch. Post-lift the callsite matches
3212        // directly on `Option<&str>` (Some("Failed") vs _); this test
3213        // pins that the borrow-form primitive plus the `.unwrap_or("")`
3214        // fallback reproduces the pre-lift bare-string shape on both
3215        // corners.
3216        let p = process_with_annotation("tatara.pleme.io/released-from", "Failed");
3217        assert_eq!(
3218            p.annotation("tatara.pleme.io/released-from").unwrap_or(""),
3219            "Failed"
3220        );
3221        let mut q = Process::new("y", empty_spec());
3222        q.metadata.annotations = None;
3223        assert_eq!(
3224            q.annotation("tatara.pleme.io/released-from").unwrap_or(""),
3225            ""
3226        );
3227    }
3228
3229    #[test]
3230    fn annotation_composes_borrow_equality_tail_matching_pre_lift_pool() {
3231        // Pins the exact tail shape `tatara-pool-reconciler::
3232        // controller_pool::process_belongs_to_pool` composed pre-lift:
3233        // an `Option<&str>` compared with `== Some(pool_name)` for the
3234        // membership gate. Post-lift the callsite composes
3235        // `p.annotation(POOL) == Some(pool_name)` verbatim; this test
3236        // pins that the borrow-form primitive returns exactly the
3237        // shape the equality gate expects.
3238        let p = process_with_annotation("tatara.pleme.io/pool", "demo-pool");
3239        assert_eq!(
3240            p.annotation("tatara.pleme.io/pool") == Some("demo-pool"),
3241            true
3242        );
3243        assert_eq!(p.annotation("tatara.pleme.io/pool") == Some("other"), false);
3244    }
3245
3246    // ─── Process::uid_or_empty substrate pins ──────────────────────────
3247    //
3248    // Pins the borrow-form metadata-projection primitive on the
3249    // `metadata.uid` axis that owns the `.metadata.uid.as_deref()
3250    // .unwrap_or("")` chain the two hand-authored
3251    // `tatara-reconciler::render` sites (`render_routing` +
3252    // `render_export_jobs`) restated by hand pre-lift. Peer to the
3253    // sibling `namespace_or_default_*` + `name_or_placeholder_*` pin
3254    // families on the metadata-slot × fallback-shape axis; all three
3255    // primitives return borrows of an owned-metadata slot with a slot-
3256    // specific fallback baked in (`"default"` for namespace, `"unnamed"`
3257    // for name, `""` for uid — the load-bearing gate value for
3258    // `owner_references_json`'s `is_empty` check). Fail-before-pass-
3259    // after granularity: `uid_or_empty` did not exist pre-lift, so any
3260    // test invoking it fails to compile pre-lift and passes post-lift.
3261
3262    #[test]
3263    fn uid_or_empty_returns_empty_string_when_metadata_uid_is_none() {
3264        // Empty-slot corner pin: the primitive collapses the no-uid
3265        // case to `""`, matching the pre-lift `.as_deref().unwrap_or("")`
3266        // chain's `""` byte-identically at both render consumer sites.
3267        // Semantically corresponds to a Process pre-metadata (fixtured
3268        // in tests, or caught mid-Forking before the API server has
3269        // stamped a `uid`); the downstream `owner_references_json`
3270        // composer gates on this exact `""` sentinel to stamp
3271        // `metadata.ownerReferences: []` rather than emit an owner-ref
3272        // pointing at a placeholder uid.
3273        let mut p = Process::new("scratch", empty_spec());
3274        p.metadata.uid = None;
3275        assert_eq!(p.uid_or_empty(), "");
3276    }
3277
3278    #[test]
3279    fn uid_or_empty_returns_borrowed_str_when_slot_is_populated() {
3280        // Happy-path pin: with a populated `metadata.uid` slot, the
3281        // primitive returns a borrowed `&str` whose contents match the
3282        // persisted `String`. A regression that reshaped / normalized
3283        // / cross-cluster-stripped the uid without touching this pin
3284        // would surface here rather than as silent skew at the two
3285        // `owner_references_json(name, uid)` emitters on the SAME
3286        // Process.
3287        let mut p = Process::new("owned-proc", empty_spec());
3288        p.metadata.uid = Some("uid-abc-123".into());
3289        assert_eq!(p.uid_or_empty(), "uid-abc-123");
3290    }
3291
3292    #[test]
3293    fn uid_or_empty_returns_empty_string_when_slot_is_explicitly_empty_string() {
3294        // Corner between the missing-slot `None` and the explicitly-
3295        // empty-string `Some("")` — both collapse to `""` at the
3296        // primitive because the downstream gate at
3297        // `owner_references_json` treats `.is_empty()` uniformly (the
3298        // empty-slot posture is what the whole primitive family
3299        // encodes: "no admissible owner reference, stamp `[]`"). A
3300        // regression that discriminated the two corners (returning a
3301        // sentinel `"<none>"` for the missing slot but `""` for the
3302        // explicit slot) would break the composition with
3303        // `owner_references_json` at the exactly-two-corner gate.
3304        let mut p = Process::new("owned-proc", empty_spec());
3305        p.metadata.uid = Some(String::new());
3306        assert_eq!(p.uid_or_empty(), "");
3307    }
3308
3309    #[test]
3310    fn uid_or_empty_is_a_zero_copy_borrow_projection() {
3311        // Borrow-discipline pin: the returned `&str` borrows the
3312        // persisted `String`'s underlying byte buffer in place — NOT
3313        // a fresh allocation or a clone. A regression that switched
3314        // the projection to an owned `String` (via `.clone()` or a
3315        // `format!` wrap) would defeat the zero-copy contract the
3316        // lift's primary strict-widening delivers, and would surface
3317        // here via pointer-identity comparison.
3318        let mut p = Process::new("owned-proc", empty_spec());
3319        p.metadata.uid = Some("uid-borrow-pin".into());
3320        let slice = p.uid_or_empty();
3321        assert!(std::ptr::eq(
3322            slice.as_ptr(),
3323            p.metadata.uid.as_ref().unwrap().as_ptr()
3324        ));
3325    }
3326
3327    #[test]
3328    fn uid_or_empty_is_a_pure_projection() {
3329        // Purity pin — repeated calls return byte-identical slices
3330        // (same pointer, same length). A regression that introduced
3331        // state (a lazy-cached normalized slot, a first-call
3332        // canonicalization pass) would surface here rather than as
3333        // silent drift between the two render consumer sites on the
3334        // SAME Process within one render pass.
3335        let mut p = Process::new("owned-proc", empty_spec());
3336        p.metadata.uid = Some("uid-pure".into());
3337        let a = p.uid_or_empty();
3338        let b = p.uid_or_empty();
3339        assert!(std::ptr::eq(a.as_ptr(), b.as_ptr()));
3340        assert_eq!(a.len(), b.len());
3341    }
3342
3343    #[test]
3344    fn uid_or_empty_matches_pre_lift_render_chain_shape() {
3345        // Byte-identical parity pin between the borrow-form primitive
3346        // here and the pre-lift `tatara-reconciler::render` chain shape
3347        // — the exact `.metadata.uid.as_deref().unwrap_or("")`
3348        // incantation both `render_routing` (line 514) and
3349        // `render_export_jobs` (line 653) spelled by hand pre-lift.
3350        // Sweeps every corner (missing uid slot, populated uid slot,
3351        // explicitly-empty uid slot) so a regression that inserted a
3352        // normalization the pre-lift chain does NOT apply — or vice
3353        // versa — surfaces here rather than as silent drift between
3354        // the ONE substrate owner and the two consumer sites.
3355        fn pre_lift(p: &Process) -> &str {
3356            p.metadata.uid.as_deref().unwrap_or("")
3357        }
3358        // Missing slot.
3359        let mut p = Process::new("x", empty_spec());
3360        p.metadata.uid = None;
3361        assert_eq!(p.uid_or_empty(), pre_lift(&p));
3362        // Populated slot.
3363        let mut p = Process::new("x", empty_spec());
3364        p.metadata.uid = Some("uid-42".into());
3365        assert_eq!(p.uid_or_empty(), pre_lift(&p));
3366        // Explicitly-empty slot.
3367        let mut p = Process::new("x", empty_spec());
3368        p.metadata.uid = Some(String::new());
3369        assert_eq!(p.uid_or_empty(), pre_lift(&p));
3370    }
3371
3372    #[test]
3373    fn uid_or_empty_composes_with_owner_references_json_empty_gate() {
3374        // Cross-primitive composition pin — the empty-string sentinel
3375        // this primitive returns for the missing-uid corner is EXACTLY
3376        // the sentinel the sibling substrate composer
3377        // `owner_references_json(name, uid)` gates on to stamp
3378        // `metadata.ownerReferences: []`. A regression that changed
3379        // the sentinel at either end (this primitive returning
3380        // `"<none>"`, `owner_references_json` gating on `uid == "0"`
3381        // instead of `uid.is_empty()`) would break the composition
3382        // and surface here rather than as an operator-observed
3383        // orphan resource after apply.
3384        let mut p = Process::new("x", empty_spec());
3385        p.metadata.uid = None;
3386        let refs = crate::owner_references_json("some-name", p.uid_or_empty());
3387        assert!(
3388            refs.is_empty(),
3389            "empty-uid corner must produce empty owner-refs array"
3390        );
3391
3392        p.metadata.uid = Some("real-uid".into());
3393        let refs = crate::owner_references_json("some-name", p.uid_or_empty());
3394        assert_eq!(
3395            refs.len(),
3396            1,
3397            "populated-uid corner must produce one owner-ref entry"
3398        );
3399    }
3400
3401    // ─── Process::owned_name_or_empty substrate pins ─────────────────
3402    //
3403    // Pins the owned-form metadata-projection primitive on the
3404    // `metadata.name` axis that owns the
3405    // `.metadata.name.clone().unwrap_or_default()` chain the two hand-
3406    // authored `tatara-pool-reconciler::controller_pool` sites (the
3407    // `PoolMember` seed at line 68 + the `PoolMemberSnapshot` desired-
3408    // count seed at line 108) restated by hand pre-lift. Peer to the
3409    // sibling `uid_or_empty` pin family on the (return-form × fallback-
3410    // value) axis pair — `uid_or_empty` owns the BORROW + empty-sentinel
3411    // corner (`&str` for owner-ref emitters gating on `.is_empty()`);
3412    // this method owns the OWNED + empty-sentinel corner (`String` for
3413    // struct-literal / HashMap-key row-builder consumers whose
3414    // downstream fills a `String` field with the load-bearing `""`
3415    // sentinel). Fail-before-pass-after granularity: `owned_name_or_empty`
3416    // did not exist pre-lift, so any test invoking it fails to compile
3417    // pre-lift and passes post-lift.
3418
3419    #[test]
3420    fn owned_name_or_empty_returns_empty_string_when_metadata_name_is_none() {
3421        // Empty-slot corner pin: the primitive collapses the no-name
3422        // case to `String::new()`, matching the pre-lift
3423        // `.clone().unwrap_or_default()` chain's empty `String` byte-
3424        // identically at both pool-reconciler consumer sites.
3425        // Semantically corresponds to a Process pre-metadata-name (test
3426        // fixture, dynamic API response pre-name-resolution); the
3427        // downstream `PoolMember { process_name, .. }` slot then holds
3428        // `""` as a stable "no name to key by" signal rather than a
3429        // display placeholder that would silently alias distinct rows.
3430        let mut p = Process::new("scratch", empty_spec());
3431        p.metadata.name = None;
3432        assert_eq!(p.owned_name_or_empty(), String::new());
3433    }
3434
3435    #[test]
3436    fn owned_name_or_empty_returns_owned_string_when_slot_is_populated() {
3437        // Happy-path pin: with a populated `metadata.name` slot, the
3438        // primitive returns an owned `String` whose contents match the
3439        // persisted `String`. A regression that reshaped / normalized
3440        // / case-folded the name without touching this pin would surface
3441        // here rather than as silent skew between the two pool-member
3442        // seeds keying on the SAME Process's name.
3443        let p = Process::new("api", empty_spec());
3444        assert_eq!(p.owned_name_or_empty(), "api");
3445    }
3446
3447    #[test]
3448    fn owned_name_or_empty_returns_empty_string_when_slot_is_explicitly_empty_string() {
3449        // Corner between the missing-slot `None` and the explicitly-
3450        // empty-string `Some(String::new())` — both collapse to `""` at
3451        // the primitive because the downstream pool-member consumers
3452        // treat both corners uniformly (no name, no key). A regression
3453        // that discriminated the two corners (returning a sentinel
3454        // `"<none>"` for the missing slot but `""` for the explicit
3455        // slot) would break `String::is_empty` gating at the row-builder
3456        // callsites without moving this pin.
3457        let mut p = Process::new("scratch", empty_spec());
3458        p.metadata.name = Some(String::new());
3459        assert_eq!(p.owned_name_or_empty(), String::new());
3460        assert!(p.owned_name_or_empty().is_empty());
3461    }
3462
3463    #[test]
3464    fn owned_name_or_empty_is_a_pure_projection() {
3465        // Purity pin — repeated calls return byte-identical `String`
3466        // values. A regression that introduced state (a lazy-cached
3467        // normalized slot, a first-call canonicalization pass) would
3468        // surface here rather than as silent drift between the pool-
3469        // member seed and the desired-count snapshot seed on the SAME
3470        // Process within one reconcile pass.
3471        let p = Process::new("stable-name", empty_spec());
3472        assert_eq!(p.owned_name_or_empty(), p.owned_name_or_empty());
3473    }
3474
3475    #[test]
3476    fn owned_name_or_empty_returns_independent_owned_string() {
3477        // Owned-discipline pin: the returned `String` is an independent
3478        // allocation the caller may consume, `.push_str` into, or move
3479        // into a struct-literal `process_name: String` slot — NOT a
3480        // shared reference into `metadata.name`. A regression that
3481        // switched the projection to a `Cow`-shaped variant or a slice-
3482        // form projection would defeat the owned-form contract the two
3483        // pool-reconciler struct-literal consumers depend on (a slice
3484        // cannot land in a `process_name: String` slot without a re-
3485        // clone), and would surface here at compile time via the mutate-
3486        // in-place test below.
3487        let p = Process::new("owned-proc", empty_spec());
3488        let mut owned = p.owned_name_or_empty();
3489        owned.push_str("-mutated");
3490        assert_eq!(owned, "owned-proc-mutated");
3491        // The Process's own slot is unchanged — the returned String
3492        // owns its own byte buffer, disjoint from `metadata.name`.
3493        assert_eq!(p.metadata.name.as_deref(), Some("owned-proc"));
3494    }
3495
3496    #[test]
3497    fn owned_name_or_empty_matches_pre_lift_controller_pool_chain_shape() {
3498        // Byte-identical parity pin between the owned-form primitive
3499        // here and the pre-lift `tatara-pool-reconciler::controller_pool`
3500        // chain shape — the exact `.metadata.name.clone().unwrap_or_default()`
3501        // incantation both `PoolMember` seed (line 68) and
3502        // `PoolMemberSnapshot` seed (line 108) spelled by hand pre-lift.
3503        // Sweeps every corner (missing name slot, populated name slot,
3504        // explicitly-empty name slot) so a regression that inserted a
3505        // normalization the pre-lift chain does NOT apply — or vice
3506        // versa — surfaces here rather than as silent drift between
3507        // the ONE substrate owner and the two consumer sites.
3508        fn pre_lift(p: &Process) -> String {
3509            p.metadata.name.clone().unwrap_or_default()
3510        }
3511        // Missing slot.
3512        let mut p = Process::new("x", empty_spec());
3513        p.metadata.name = None;
3514        assert_eq!(p.owned_name_or_empty(), pre_lift(&p));
3515        // Populated slot.
3516        let p = Process::new("real-name", empty_spec());
3517        assert_eq!(p.owned_name_or_empty(), pre_lift(&p));
3518        // Explicitly-empty slot.
3519        let mut p = Process::new("x", empty_spec());
3520        p.metadata.name = Some(String::new());
3521        assert_eq!(p.owned_name_or_empty(), pre_lift(&p));
3522    }
3523
3524    #[test]
3525    fn owned_name_or_empty_shares_empty_sentinel_with_uid_or_empty() {
3526        // Cross-primitive coherence pin — the empty-string fallback this
3527        // primitive returns for the missing-name corner is the SAME
3528        // sentinel the sibling borrow-form primitive `uid_or_empty`
3529        // returns for the missing-uid corner. Both partition the OWNED
3530        // × BORROW corner of the metadata-slot family on identical
3531        // fallback semantics ("the slot is unset"), so a consumer that
3532        // switches between them based on downstream ownership
3533        // requirements never sees a different missing-slot spelling as
3534        // a side effect. A regression that drifted either sentinel
3535        // (this primitive returning `"<unnamed>"`, `uid_or_empty`
3536        // returning `"<none>"`) would break the partition and surface
3537        // here rather than as silent shape drift across the family.
3538        let mut p = Process::new("scratch", empty_spec());
3539        p.metadata.name = None;
3540        p.metadata.uid = None;
3541        assert_eq!(p.owned_name_or_empty(), p.uid_or_empty());
3542        assert!(p.owned_name_or_empty().is_empty());
3543        assert!(p.uid_or_empty().is_empty());
3544    }
3545
3546    #[test]
3547    fn owned_name_or_empty_returns_distinct_fallback_from_name_or_placeholder() {
3548        // Axis-partition pin — the owned + empty-sentinel primitive here
3549        // and the borrow + display-placeholder primitive
3550        // [`Self::name_or_placeholder`] MUST return distinct fallback
3551        // values on the missing-name corner. The distinction is load-
3552        // bearing: `owned_name_or_empty` is for HashMap-key / row-builder
3553        // consumers that need distinct keys for missing-name Processes
3554        // (empty string collides only with other missing-name rows,
3555        // never with a real "unnamed" Process); `name_or_placeholder`
3556        // is for log-line / display consumers that render the
3557        // `"unnamed"` word to operators. A regression that unified the
3558        // two fallbacks (either primitive returning the other's
3559        // sentinel) would silently collapse missing-name pool members
3560        // into a display-string key or expose the empty sentinel to
3561        // operator log lines. This pin catches either drift.
3562        let mut p = Process::new("scratch", empty_spec());
3563        p.metadata.name = None;
3564        assert_eq!(p.owned_name_or_empty(), "");
3565        assert_eq!(p.name_or_placeholder(), Process::UNNAMED_PLACEHOLDER);
3566        assert_ne!(p.owned_name_or_empty(), p.name_or_placeholder());
3567    }
3568
3569    // ─── Process::declared_parent_pid substrate pins ─────────────────
3570    //
3571    // Pins the borrow-form spec-projection primitive on the declared
3572    // parent-PID axis that owns the `.spec.identity.parent.as_deref()`
3573    // chain the two hand-authored `tatara-reconciler::phase_machine`
3574    // sites (`handle_forking` ALLOCATE-PID composer + `handle_exiting`
3575    // SIGTERM-cascade child-fan-out filter) restated by hand pre-lift.
3576    // Peer to the sibling `observed_pid_*` pin family on the (spec-
3577    // declared × status-observed) axis pair; both compose the same
3578    // borrow-form `Option<&str>` return-shape skeleton on distinct
3579    // slots (`spec.identity.parent` vs. `status.pid`). Fail-before-
3580    // pass-after granularity: `declared_parent_pid` did not exist
3581    // pre-lift, so any test invoking it fails to compile pre-lift and
3582    // passes post-lift.
3583    fn process_with_declared_parent(parent: Option<&str>) -> Process {
3584        let mut spec = empty_spec();
3585        spec.identity.parent = parent.map(str::to_string);
3586        Process::new("child-proc", spec)
3587    }
3588
3589    #[test]
3590    fn declared_parent_pid_returns_none_when_slot_is_none() {
3591        // Empty-slot corner pin: the primitive collapses the no-
3592        // parent case to `None`, matching the pre-lift `.as_deref()`
3593        // chain's `None` byte-identically at both reconciler consumer
3594        // sites. Semantically corresponds to a Process authored at
3595        // cluster init (PID 1) with no upstream parent — the
3596        // ALLOCATE-PID composer feeds `None` into `pid::allocate_pid`
3597        // to signal "no prefix", and the SIGTERM cascade's filter
3598        // never matches such a Process because a child's declared
3599        // parent can never equal `Some(pid)` when the slot is `None`.
3600        let p = process_with_declared_parent(None);
3601        assert!(p.declared_parent_pid().is_none());
3602    }
3603
3604    #[test]
3605    fn declared_parent_pid_returns_borrowed_str_when_slot_is_populated() {
3606        // Happy-path pin: with a populated `spec.identity.parent`
3607        // slot, the primitive returns a borrowed `&str` whose
3608        // contents match the persisted `String`. A regression that
3609        // filtered / reshaped / canonicalized the string would
3610        // surface here rather than as silent skew at the child-fan-
3611        // out filter's `.declared_parent_pid() == Some(pid)`
3612        // equality check on the SAME parent-child pair.
3613        let p = process_with_declared_parent(Some("seph.1"));
3614        assert_eq!(p.declared_parent_pid(), Some("seph.1"));
3615    }
3616
3617    #[test]
3618    fn declared_parent_pid_is_a_zero_copy_borrow_projection() {
3619        // Borrow-discipline pin: the returned `&str` borrows the
3620        // persisted `String`'s underlying byte buffer in place —
3621        // NOT a fresh allocation or a clone. A regression that
3622        // switched the projection to an owned `String` (via
3623        // `.clone()` or `.to_owned()`) would defeat the zero-copy
3624        // contract the lift's primary strict-widening delivers.
3625        // The `handle_exiting` cascade filter runs per candidate
3626        // child across the cluster-wide Process list; a per-row
3627        // `String::clone` would allocate one heap block per non-
3628        // matching row, so the borrow-form primitive is load-
3629        // bearing for large clusters. Peer to the sibling
3630        // `observed_pid_is_a_zero_copy_borrow_projection` pin on
3631        // the status-observed side of the axis pair.
3632        let p = process_with_declared_parent(Some("seph.1"));
3633        let borrowed = p.declared_parent_pid().expect("populated slot");
3634        let persisted = p.spec.identity.parent.as_ref().unwrap();
3635        assert!(std::ptr::eq(borrowed.as_ptr(), persisted.as_ptr()));
3636    }
3637
3638    #[test]
3639    fn declared_parent_pid_is_a_pure_projection() {
3640        // Purity pin: calling the projection twice on the same
3641        // `Process` returns byte-identical `&str`s (same pointer,
3642        // same length). A regression that introduced state — a
3643        // lazy-cached slice materialized on first call, a
3644        // normalization step that ran once and cached — would
3645        // surface here rather than as silent drift between the
3646        // ALLOCATE-PID composer and the SIGTERM cascade's child-
3647        // fan-out filter within one reconcile pass.
3648        let p = process_with_declared_parent(Some("seph.1.3"));
3649        let a = p.declared_parent_pid().expect("populated slot");
3650        let b = p.declared_parent_pid().expect("populated slot");
3651        assert!(std::ptr::eq(a.as_ptr(), b.as_ptr()));
3652        assert_eq!(a.len(), b.len());
3653    }
3654
3655    #[test]
3656    fn declared_parent_pid_matches_pre_lift_reconciler_chain_shape() {
3657        // Byte-identical parity pin between the borrow-form primitive
3658        // here and the pre-lift `tatara-reconciler::phase_machine`
3659        // `.spec.identity.parent.as_deref()` chain shape. Sweeps
3660        // every corner every callsite plausibly encounters (empty
3661        // slot, populated with a hierarchical PID). A regression
3662        // that inserted a normalization step at the primitive the
3663        // pre-lift chain does NOT apply — or vice versa — surfaces
3664        // here rather than as silent drift between the pre-lift
3665        // consumer sites and the ONE substrate owner they now route
3666        // through. Peer to
3667        // `observed_pid_matches_pre_lift_reconciler_chain_shape` on
3668        // the sibling axis's borrow-form primitive.
3669        fn pre_lift(p: &Process) -> Option<&str> {
3670            p.spec.identity.parent.as_deref()
3671        }
3672        // Empty slot.
3673        let p = process_with_declared_parent(None);
3674        assert_eq!(p.declared_parent_pid(), pre_lift(&p));
3675        // Populated with a hierarchical PID.
3676        let p = process_with_declared_parent(Some("seph.1"));
3677        assert_eq!(p.declared_parent_pid(), pre_lift(&p));
3678        // Populated with a deeper hierarchical PID.
3679        let p = process_with_declared_parent(Some("seph.1.7.42"));
3680        assert_eq!(p.declared_parent_pid(), pre_lift(&p));
3681    }
3682
3683    #[test]
3684    fn declared_parent_pid_preserves_hierarchical_pid_format() {
3685        // Format-preservation pin: the hierarchical PID path
3686        // (dotted-segment form `seph.1.7`, matching the ported
3687        // `convergence-controller/src/identity.rs` scheme) reaches
3688        // the caller with segments and separators byte-identical
3689        // to the persisted `String`. A regression that inserted a
3690        // canonicalization pass (a segment-count validator, a
3691        // separator swap `.` → `/`, a leading/trailing whitespace
3692        // trim) would silently misroute the SIGTERM cascade's
3693        // `declared_parent_pid() == Some(pid)` comparator against
3694        // children whose `parent` field was authored in the ported
3695        // scheme's exact form — the SAME children the observed_pid
3696        // primitive is pinned to match on the other side of the
3697        // axis pair.
3698        for parent in ["seph", "seph.1", "seph.1.7", "seph.1.7.42"] {
3699            let p = process_with_declared_parent(Some(parent));
3700            assert_eq!(p.declared_parent_pid(), Some(parent));
3701        }
3702    }
3703
3704    #[test]
3705    fn declared_parent_pid_composes_with_observed_pid_for_child_fanout_filter() {
3706        // Cross-axis coherence pin against the sibling
3707        // [`Self::observed_pid`] on the (spec-declared × status-
3708        // observed) axis pair: a child's `.declared_parent_pid()`
3709        // and its parent's `.observed_pid()` compose through the
3710        // SAME borrow-form `Option<&str>` skeleton so the
3711        // `handle_exiting` cascade filter's equality gate holds
3712        // structurally. A regression that skewed EITHER primitive's
3713        // return-form (return-shape, borrow discipline, empty-slot
3714        // collapse) would silently misroute every SIGTERM cascade
3715        // on the parent-child pair. This pin re-reads both primitives
3716        // at test time so the composition holds iff both live paths
3717        // are the current implementation.
3718        // Parent Process: has an observed PID.
3719        let mut parent = Process::new("parent-proc", empty_spec());
3720        parent.status = Some(ProcessStatus {
3721            pid: Some("seph.1".to_string()),
3722            ..Default::default()
3723        });
3724        // Child Process: declared parent matches parent's observed PID.
3725        let child = process_with_declared_parent(Some("seph.1"));
3726        // The `handle_exiting` filter's equality gate:
3727        // `child.declared_parent_pid() == Some(parent.observed_pid()?)`.
3728        let parent_pid = parent.observed_pid().expect("parent has PID");
3729        assert_eq!(child.declared_parent_pid(), Some(parent_pid));
3730        // Sibling Process with an unrelated declared parent must NOT
3731        // match the same parent — pins that the filter's SKIP branch
3732        // holds on the other side of the axis pair.
3733        let sibling = process_with_declared_parent(Some("seph.2"));
3734        assert_ne!(sibling.declared_parent_pid(), Some(parent_pid));
3735    }
3736
3737    // ─── Process::declared_name_override substrate pins ──────────────
3738    //
3739    // Pins the borrow-form spec-projection primitive on the declared
3740    // name-override sub-axis of the declared-identity axis that owns
3741    // the `.spec.identity.name_override.as_deref()` chain the two
3742    // hand-authored `tatara-reconciler::phase_machine` sites
3743    // (`handle_pending` DECLARE composer + `handle_forking` ALLOCATE-
3744    // PID rehydration branch) restated by hand pre-lift. Peer to the
3745    // sibling `declared_parent_pid_*` pin family on the (parent ×
3746    // name-override) sub-axis pair; both compose the same borrow-form
3747    // `Option<&str>` return-shape skeleton on distinct slots
3748    // (`spec.identity.name_override` vs `spec.identity.parent`).
3749    // Fail-before-pass-after granularity: `declared_name_override`
3750    // did not exist pre-lift, so any test invoking it fails to
3751    // compile pre-lift and passes post-lift.
3752    fn process_with_declared_name_override(name_override: Option<&str>) -> Process {
3753        let mut spec = empty_spec();
3754        spec.identity.name_override = name_override.map(str::to_string);
3755        Process::new("some-proc", spec)
3756    }
3757
3758    #[test]
3759    fn declared_name_override_returns_none_when_slot_is_none() {
3760        // Empty-slot corner pin: the primitive collapses the no-
3761        // override case to `None`, matching the pre-lift `.as_deref()`
3762        // chain's `None` byte-identically at both reconciler consumer
3763        // sites. Semantically corresponds to a Process authored
3764        // WITHOUT the human-name-override escape hatch — the default;
3765        // `derive_identity` then computes the name from the content
3766        // hash and stamps `name_override: false` on the resulting
3767        // [`Identity`].
3768        let p = process_with_declared_name_override(None);
3769        assert!(p.declared_name_override().is_none());
3770    }
3771
3772    #[test]
3773    fn declared_name_override_returns_borrowed_str_when_slot_is_populated() {
3774        // Happy-path pin: with a populated `spec.identity
3775        // .name_override` slot, the primitive returns a borrowed
3776        // `&str` whose contents match the persisted `String`. A
3777        // regression that filtered / reshaped / canonicalized the
3778        // string at the primitive (as opposed to inside
3779        // `derive_identity`, where the trim/empty-filter lives today)
3780        // would surface here rather than as silent skew between the
3781        // DECLARE composer and the ALLOCATE-PID rehydration branch on
3782        // the SAME Process spec.
3783        let p = process_with_declared_name_override(Some("observability-stack"));
3784        assert_eq!(p.declared_name_override(), Some("observability-stack"));
3785    }
3786
3787    #[test]
3788    fn declared_name_override_is_a_zero_copy_borrow_projection() {
3789        // Borrow-discipline pin: the returned `&str` borrows the
3790        // persisted `String`'s underlying byte buffer in place —
3791        // NOT a fresh allocation or a clone. Peer to the sibling
3792        // `declared_parent_pid_is_a_zero_copy_borrow_projection` pin
3793        // on the other side of the (parent × name-override) sub-axis
3794        // pair; the borrow discipline holds structurally on BOTH
3795        // sub-axes so a future `declared_identity` composite that
3796        // returns both halves together can compose them without
3797        // dropping into an owning form.
3798        let p = process_with_declared_name_override(Some("observability-stack"));
3799        let borrowed = p.declared_name_override().expect("populated slot");
3800        let persisted = p.spec.identity.name_override.as_ref().unwrap();
3801        assert!(std::ptr::eq(borrowed.as_ptr(), persisted.as_ptr()));
3802    }
3803
3804    #[test]
3805    fn declared_name_override_is_a_pure_projection() {
3806        // Purity pin: calling the projection twice on the same
3807        // `Process` returns byte-identical `&str`s (same pointer,
3808        // same length). A regression that introduced state — a
3809        // lazy-cached slice materialized on first call, a
3810        // normalization step that ran once and cached — would
3811        // surface here rather than as silent drift between the
3812        // DECLARE composer and the ALLOCATE-PID rehydration branch
3813        // within one reconcile pass.
3814        let p = process_with_declared_name_override(Some("gateway-primary"));
3815        let a = p.declared_name_override().expect("populated slot");
3816        let b = p.declared_name_override().expect("populated slot");
3817        assert!(std::ptr::eq(a.as_ptr(), b.as_ptr()));
3818        assert_eq!(a.len(), b.len());
3819    }
3820
3821    #[test]
3822    fn declared_name_override_matches_pre_lift_reconciler_chain_shape() {
3823        // Byte-identical parity pin between the borrow-form primitive
3824        // here and the pre-lift `tatara-reconciler::phase_machine`
3825        // `.spec.identity.name_override.as_deref()` chain shape.
3826        // Sweeps every corner every callsite plausibly encounters
3827        // (empty slot, populated with a bare name, populated with a
3828        // whitespace-containing name that `derive_identity`'s
3829        // internal trim would collapse, populated with an explicitly
3830        // empty string that `derive_identity`'s internal
3831        // `!s.is_empty()` filter would reject). A regression that
3832        // inserted a normalization step at the primitive the pre-
3833        // lift chain does NOT apply — or vice versa — surfaces here
3834        // rather than as silent drift between the pre-lift consumer
3835        // sites and the ONE substrate owner they now route through.
3836        // Peer to
3837        // `declared_parent_pid_matches_pre_lift_reconciler_chain_shape`
3838        // on the sibling sub-axis's borrow-form primitive.
3839        fn pre_lift(p: &Process) -> Option<&str> {
3840            p.spec.identity.name_override.as_deref()
3841        }
3842        // Empty slot.
3843        let p = process_with_declared_name_override(None);
3844        assert_eq!(p.declared_name_override(), pre_lift(&p));
3845        // Populated with a bare name.
3846        let p = process_with_declared_name_override(Some("observability-stack"));
3847        assert_eq!(p.declared_name_override(), pre_lift(&p));
3848        // Populated with a whitespace-containing name.
3849        let p = process_with_declared_name_override(Some("  observability-stack  "));
3850        assert_eq!(p.declared_name_override(), pre_lift(&p));
3851        // Populated with an explicitly empty string. Distinct from
3852        // the missing-slot `None` corner both at the primitive here
3853        // and at the pre-lift chain (the trim/filter that collapses
3854        // these two into the same `false`-branched
3855        // `Identity { name_override: false, .. }` lives INSIDE
3856        // `derive_identity`, NOT at the borrow site) — the primitive
3857        // MUST preserve the distinction so a future lift of the trim/
3858        // filter OUT of `derive_identity` INTO the primitive is a
3859        // conscious substrate change, not a silent one.
3860        let p = process_with_declared_name_override(Some(""));
3861        assert_eq!(p.declared_name_override(), pre_lift(&p));
3862    }
3863
3864    #[test]
3865    fn declared_name_override_preserves_raw_slot_verbatim() {
3866        // Invariance-under-`derive_identity`-normalization pin: the
3867        // primitive returns the slot's raw byte contents verbatim —
3868        // no trim, no empty-string filter, no case fold, no
3869        // normalization of any kind. `derive_identity` internally
3870        // applies `.map(str::trim).filter(|s| !s.is_empty())` before
3871        // dispatching on `Some(non_empty)` vs `None | Some(empty |
3872        // whitespace)`, but that transform lives IN `derive_identity`,
3873        // NOT at the borrow site. A regression that pulled the trim/
3874        // filter forward INTO the primitive would silently collapse
3875        // three currently-distinct corners at the borrow site (bare
3876        // populated → `Some(name)`; whitespace-only → `Some("   ")`;
3877        // empty → `Some("")`) into two (bare → `Some(name)`; the
3878        // other two → `None`). That collapse might be an intentional
3879        // substrate change some future run wants to make; if so, it
3880        // lands as a conscious edit here (with this pin updated in
3881        // the same commit) rather than as silent behavior drift.
3882        for value in ["bare", "  padded  ", "\ttabs\t", "   ", ""] {
3883            let p = process_with_declared_name_override(Some(value));
3884            assert_eq!(
3885                p.declared_name_override(),
3886                Some(value),
3887                "declared_name_override must preserve raw slot verbatim for value {value:?}"
3888            );
3889        }
3890    }
3891
3892    #[test]
3893    fn declared_name_override_composes_with_derive_identity_call_shape() {
3894        // Cross-primitive coherence pin against the [`derive_identity`]
3895        // consumer: the two live `tatara-reconciler::phase_machine`
3896        // callsites feed `p.declared_name_override()` as the second
3897        // positional argument to `derive_identity(&p.spec, …)`. This
3898        // pin exercises that exact call shape at test time so a
3899        // regression that skewed the primitive's return-form (return-
3900        // shape, borrow discipline, empty-slot collapse) surfaces
3901        // here as a shape mismatch at the [`derive_identity`] call
3902        // site rather than as silent operator-facing skew between the
3903        // DECLARE composer and the ALLOCATE-PID rehydration branch.
3904        // Populated with a bare non-empty name: `derive_identity`
3905        // dispatches on `Some(non_empty)` and stamps
3906        // `name_override: true` on the resulting [`Identity`], with
3907        // the resulting `.name` equal to the raw slot value.
3908        let p = process_with_declared_name_override(Some("gateway-primary"));
3909        let id = crate::identity::derive_identity(&p.spec, p.declared_name_override());
3910        assert!(id.name_override);
3911        assert_eq!(id.name, "gateway-primary");
3912        // Empty slot: `derive_identity` dispatches on `None` and
3913        // stamps `name_override: false` on the resulting [`Identity`],
3914        // with the resulting `.name` derived from the content hash
3915        // (NOT equal to any operator-authored slot value).
3916        let p = process_with_declared_name_override(None);
3917        let id = crate::identity::derive_identity(&p.spec, p.declared_name_override());
3918        assert!(!id.name_override);
3919    }
3920
3921    // ─── Process::observed_flux_resources substrate pins ───────────────
3922    //
3923    // Pins the borrow-form status-projection primitive that owns the
3924    // 5-line `.status.as_ref().map(|s| s.flux_resources.clone())
3925    // .unwrap_or_default()` chain the two hand-authored
3926    // `tatara-reconciler::phase_machine` sites (`handle_running` +
3927    // `handle_attested`) restated by hand pre-lift. Fail-before-pass-
3928    // after granularity: a regression that widened the missing-`status`
3929    // corner, dropped the slot, or drifted the borrow discipline
3930    // surfaces here rather than as silent operator-facing skew between
3931    // the VERIFY-phase readiness probe and the ATTEST-heartbeat drift
3932    // detector.
3933
3934    fn sample_flux_ref(name: &str) -> FluxResourceRef {
3935        // Distinct slot values so a swap between adjacent tuple
3936        // positions surfaces as an equality failure at the assertion
3937        // site — a slot-inversion regression cannot masquerade as
3938        // identity by accident. Peer to the sibling
3939        // `tatara_process::status::tests::sample_flux_ref` discipline
3940        // on the fetch-coords axis. Routes through the ONE substrate
3941        // composer [`FluxResourceRef::pending`] — the 4-slot pre-
3942        // observation-shape composer that owns the workspace-wide
3943        // `FluxResourceRef { …, ready: false, message: None,
3944        // last_check: None }` fixture literal.
3945        FluxResourceRef::pending(
3946            "kustomize.toolkit.fluxcd.io/v1",
3947            "Kustomization",
3948            name,
3949            "flux-system",
3950        )
3951    }
3952
3953    fn process_with_flux_resources(refs: Vec<FluxResourceRef>) -> Process {
3954        let mut p = Process::new("api-gateway", empty_spec());
3955        p.metadata.namespace = Some("prod".into());
3956        let mut status = ProcessStatus::default();
3957        status.flux_resources = refs;
3958        p.status = Some(status);
3959        p
3960    }
3961
3962    #[test]
3963    fn observed_flux_resources_returns_empty_slice_when_status_is_none() {
3964        // Missing-`status` corner pin: the primitive collapses the
3965        // no-status case to `&[]` so downstream `.is_empty()` /
3966        // `.len()` / iteration behave identically on a `Process`
3967        // whose status field is `None` and on one whose status
3968        // carries an empty `flux_resources` slot. Matches the
3969        // pre-lift `.unwrap_or_default()`'s empty-`Vec` corner
3970        // byte-identically at every reconciler consumer's downstream
3971        // shape.
3972        let mut p = Process::new("api", empty_spec());
3973        p.status = None;
3974        assert!(p.observed_flux_resources().is_empty());
3975        assert_eq!(p.observed_flux_resources().len(), 0);
3976    }
3977
3978    #[test]
3979    fn observed_flux_resources_returns_empty_slice_when_flux_resources_is_empty() {
3980        // Zero-refs-under-populated-status corner pin: the primitive
3981        // returns an empty slice, matching the missing-`status`
3982        // corner byte-identically. A regression that treated the two
3983        // corners differently (a `None`-vs-empty signal that
3984        // downstream consumers could grep on) would silently promote
3985        // an internal representation detail (whether the reconciler
3986        // has ever written a status subresource) into observable
3987        // behavior.
3988        let p = process_with_flux_resources(vec![]);
3989        assert!(p.observed_flux_resources().is_empty());
3990        assert_eq!(p.observed_flux_resources().len(), 0);
3991    }
3992
3993    #[test]
3994    fn observed_flux_resources_returns_slice_of_persisted_vec() {
3995        // Happy-path pin: with a populated `status.flux_resources`
3996        // slot, the primitive returns a borrowed slice whose length
3997        // and per-element identity match the persisted vector. A
3998        // regression that filtered / reshaped / deduplicated the
3999        // slice would surface here rather than as silent skew at the
4000        // downstream fetch consumers.
4001        let refs = vec![
4002            sample_flux_ref("observability-stack"),
4003            sample_flux_ref("gateway"),
4004        ];
4005        let p = process_with_flux_resources(refs.clone());
4006        let observed = p.observed_flux_resources();
4007        assert_eq!(observed.len(), 2);
4008        assert_eq!(observed[0].name, "observability-stack");
4009        assert_eq!(observed[1].name, "gateway");
4010    }
4011
4012    #[test]
4013    fn observed_flux_resources_is_a_zero_copy_borrow_projection() {
4014        // Borrow-discipline pin: the returned slice borrows the
4015        // persisted `Vec<FluxResourceRef>` in place — NOT a fresh
4016        // allocation or a clone. A regression that switched the
4017        // projection to owned refs (via `.clone()` or `.to_vec()`)
4018        // would defeat the zero-copy contract the lift's primary
4019        // strict-widening delivers (the pre-lift 5-line chain
4020        // eagerly cloned the whole vector per reconcile pass; the
4021        // post-lift primitive borrows). Peer to the sibling
4022        // `flux_resource_ref_fetch_coords_returns_borrows_of_owned_slots`
4023        // pin on the per-ref borrow-projection axis.
4024        let refs = vec![sample_flux_ref("observability-stack")];
4025        let p = process_with_flux_resources(refs);
4026        let observed = p.observed_flux_resources();
4027        let persisted = &p.status.as_ref().unwrap().flux_resources;
4028        assert!(std::ptr::eq(observed.as_ptr(), persisted.as_ptr()));
4029    }
4030
4031    #[test]
4032    fn observed_flux_resources_is_a_pure_projection() {
4033        // Purity pin: calling the projection twice on the same
4034        // `Process` returns byte-identical slices (same pointer,
4035        // same length). A regression that introduced state — a
4036        // lazy-cached slice materialized on first call, a
4037        // normalization step that ran once and cached — would
4038        // surface here rather than as silent drift between the
4039        // VERIFY-phase and ATTEST-heartbeat consumers on the SAME
4040        // `Process` within one reconcile pass.
4041        let refs = vec![sample_flux_ref("observability-stack")];
4042        let p = process_with_flux_resources(refs);
4043        let a = p.observed_flux_resources();
4044        let b = p.observed_flux_resources();
4045        assert!(std::ptr::eq(a.as_ptr(), b.as_ptr()));
4046        assert_eq!(a.len(), b.len());
4047    }
4048
4049    #[test]
4050    fn observed_flux_resources_matches_pre_lift_reconciler_chain_shape() {
4051        // Byte-identical parity pin between the borrow-form primitive
4052        // here and the pre-lift `tatara-reconciler::phase_machine`
4053        // 5-line chain shape. Sweeps every corner every callsite
4054        // plausibly encounters (missing status, empty flux_resources,
4055        // populated flux_resources with one ref, populated with
4056        // multiple refs). A regression that inserted a normalization
4057        // step at the primitive the pre-lift chain does NOT apply —
4058        // or vice versa — surfaces here rather than as silent drift
4059        // between the pre-lift consumer sites and the ONE substrate
4060        // owner they now route through. Peer to
4061        // `coordinates_or_none_matches_pre_lift_reconciler_helper_shape`
4062        // on the metadata axis's borrow-form primitive.
4063        // `FluxResourceRef` does not derive `PartialEq` — the parity
4064        // check walks the per-ref fetch-coords tuple (the same 4-slot
4065        // borrow projection every downstream fetch consumer routes
4066        // through) so a regression that reshaped ANY slot at ANY
4067        // index surfaces here through the sibling
4068        // `FluxResourceRef::fetch_coords` typed projection.
4069        fn pre_lift(p: &Process) -> Vec<FluxResourceRef> {
4070            p.status
4071                .as_ref()
4072                .map(|s| s.flux_resources.clone())
4073                .unwrap_or_default()
4074        }
4075        fn coord_shape(refs: &[FluxResourceRef]) -> Vec<(String, String, String, String)> {
4076            refs.iter()
4077                .map(|r| {
4078                    let (ns, av, kind, name) = r.fetch_coords();
4079                    (
4080                        ns.to_string(),
4081                        av.to_string(),
4082                        kind.to_string(),
4083                        name.to_string(),
4084                    )
4085                })
4086                .collect()
4087        }
4088        // Missing status.
4089        let mut p = Process::new("api", empty_spec());
4090        p.status = None;
4091        assert_eq!(
4092            coord_shape(p.observed_flux_resources()),
4093            coord_shape(&pre_lift(&p))
4094        );
4095        // Populated status, empty slot.
4096        let p = process_with_flux_resources(vec![]);
4097        assert_eq!(
4098            coord_shape(p.observed_flux_resources()),
4099            coord_shape(&pre_lift(&p))
4100        );
4101        // Populated status, one ref.
4102        let p = process_with_flux_resources(vec![sample_flux_ref("obs")]);
4103        assert_eq!(
4104            coord_shape(p.observed_flux_resources()),
4105            coord_shape(&pre_lift(&p))
4106        );
4107        // Populated status, multiple refs.
4108        let p = process_with_flux_resources(vec![
4109            sample_flux_ref("obs"),
4110            sample_flux_ref("gw"),
4111            sample_flux_ref("api"),
4112        ]);
4113        assert_eq!(
4114            coord_shape(p.observed_flux_resources()),
4115            coord_shape(&pre_lift(&p))
4116        );
4117    }
4118
4119    #[test]
4120    fn observed_flux_resources_missing_status_and_empty_slot_collapse_to_the_same_slice_shape() {
4121        // Cross-corner coherence pin: the missing-`status` corner and
4122        // the populated-empty-slot corner return slices whose
4123        // `.is_empty()` / `.len()` observations are IDENTICAL. A
4124        // regression that promoted the missing-`status` corner to
4125        // returning `None` (via a signature change) — or that widened
4126        // the empty-slot corner to a synthetic single-element slice
4127        // — would surface here rather than as silent operator-facing
4128        // divergence between a never-status-written Process and a
4129        // status-emptied Process.
4130        let mut p_no_status = Process::new("api", empty_spec());
4131        p_no_status.status = None;
4132        let p_empty_status = process_with_flux_resources(vec![]);
4133        assert_eq!(
4134            p_no_status.observed_flux_resources().len(),
4135            p_empty_status.observed_flux_resources().len()
4136        );
4137        assert_eq!(
4138            p_no_status.observed_flux_resources().is_empty(),
4139            p_empty_status.observed_flux_resources().is_empty()
4140        );
4141    }
4142
4143    #[test]
4144    fn observed_flux_resources_slice_preserves_persisted_ordering() {
4145        // Ordering-preservation pin: the borrowed slice preserves
4146        // the exact insertion order of the persisted vector — no
4147        // sort, no dedup, no reshape. A regression that inserted a
4148        // sort or reordering would silently misroute per-ref
4149        // observations at the downstream VERIFY-phase / ATTEST-
4150        // heartbeat consumers, both of which walk the slice
4151        // positionally and correlate the position to the observed
4152        // readiness.
4153        let refs = vec![
4154            sample_flux_ref("z-last"),
4155            sample_flux_ref("a-first"),
4156            sample_flux_ref("m-middle"),
4157        ];
4158        let p = process_with_flux_resources(refs);
4159        let observed = p.observed_flux_resources();
4160        assert_eq!(observed[0].name, "z-last");
4161        assert_eq!(observed[1].name, "a-first");
4162        assert_eq!(observed[2].name, "m-middle");
4163    }
4164
4165    // ─── Process::observed_pid substrate pins ─────────────────────────
4166    //
4167    // Pins the borrow-form status-projection primitive on the PID axis
4168    // that owns the 3-line `.status.as_ref().and_then(|s| s.pid.clone())`
4169    // chain the two hand-authored `tatara-reconciler::phase_machine`
4170    // sites (`handle_forking` ALLOCATE-PID gate + `handle_exiting`
4171    // SIGTERM cascade) restated by hand pre-lift. Peer to the sibling
4172    // `observed_flux_resources_*` pin family on the flux-resources
4173    // axis; both compose the missing-`status` fallback + borrow-form
4174    // return-shape skeleton on distinct `ProcessStatus` slots. Fail-
4175    // before-pass-after granularity: `observed_pid` did not exist
4176    // pre-lift, so any test invoking it fails to compile pre-lift and
4177    // passes post-lift.
4178
4179    fn process_with_pid(pid: Option<&str>) -> Process {
4180        let mut p = Process::new("api-gateway", empty_spec());
4181        p.metadata.namespace = Some("prod".into());
4182        let mut status = ProcessStatus::default();
4183        status.pid = pid.map(str::to_string);
4184        p.status = Some(status);
4185        p
4186    }
4187
4188    #[test]
4189    fn observed_pid_returns_none_when_status_is_none() {
4190        // Missing-`status` corner pin: the primitive collapses the
4191        // no-status case to `None` so downstream `.is_some()` /
4192        // `if let Some(_)` / `.map(...)` behave identically on a
4193        // `Process` whose status field is `None` and on one whose
4194        // status carries an unpopulated `pid` slot. Matches the
4195        // pre-lift `.and_then(...)` chain's `None` byte-identically
4196        // at every reconciler consumer's downstream shape.
4197        let mut p = Process::new("api", empty_spec());
4198        p.status = None;
4199        assert!(p.observed_pid().is_none());
4200    }
4201
4202    #[test]
4203    fn observed_pid_returns_none_when_pid_slot_is_none() {
4204        // Empty-slot-under-populated-status corner pin: the
4205        // primitive returns `None`, matching the missing-`status`
4206        // corner byte-identically. A regression that treated the
4207        // two corners differently (a `None`-vs-`Some("")` signal
4208        // that downstream consumers could grep on) would silently
4209        // promote an internal representation detail (whether the
4210        // reconciler has ever written a status subresource) into
4211        // observable behavior at the ALLOCATE-PID gate.
4212        let p = process_with_pid(None);
4213        assert!(p.observed_pid().is_none());
4214    }
4215
4216    #[test]
4217    fn observed_pid_returns_borrowed_str_when_pid_slot_is_populated() {
4218        // Happy-path pin: with a populated `status.pid` slot, the
4219        // primitive returns a borrowed `&str` whose contents match
4220        // the persisted `String`. A regression that filtered /
4221        // reshaped / canonicalized the string would surface here
4222        // rather than as silent skew at the downstream cascade
4223        // comparator's `.as_deref() == Some(...)` equality check.
4224        let p = process_with_pid(Some("seph.1.7"));
4225        assert_eq!(p.observed_pid(), Some("seph.1.7"));
4226    }
4227
4228    #[test]
4229    fn observed_pid_is_a_zero_copy_borrow_projection() {
4230        // Borrow-discipline pin: the returned `&str` borrows the
4231        // persisted `String`'s underlying byte buffer in place —
4232        // NOT a fresh allocation or a clone. A regression that
4233        // switched the projection to an owned `String` (via
4234        // `.clone()` or `.to_owned()`) would defeat the zero-copy
4235        // contract the lift's primary strict-widening delivers
4236        // (the pre-lift 3-line chain eagerly cloned the `String`
4237        // per reconcile pass at BOTH call sites even though the
4238        // ALLOCATE-PID gate immediately dropped the clone and the
4239        // SIGTERM cascade only re-borrowed it via `.as_str()`; the
4240        // post-lift primitive borrows). Peer to the sibling
4241        // `observed_flux_resources_is_a_zero_copy_borrow_projection`
4242        // pin on the flux-resources borrow-projection axis.
4243        let p = process_with_pid(Some("seph.1.7"));
4244        let observed = p.observed_pid().expect("populated slot");
4245        let persisted = p.status.as_ref().unwrap().pid.as_ref().unwrap();
4246        assert!(std::ptr::eq(observed.as_ptr(), persisted.as_ptr()));
4247    }
4248
4249    #[test]
4250    fn observed_pid_is_a_pure_projection() {
4251        // Purity pin: calling the projection twice on the same
4252        // `Process` returns byte-identical `&str`s (same pointer,
4253        // same length). A regression that introduced state — a
4254        // lazy-cached slice materialized on first call, a
4255        // normalization step that ran once and cached — would
4256        // surface here rather than as silent drift between the
4257        // ALLOCATE-PID gate and the SIGTERM cascade on the SAME
4258        // `Process` within one reconcile pass.
4259        let p = process_with_pid(Some("seph.1.7"));
4260        let a = p.observed_pid().expect("populated slot");
4261        let b = p.observed_pid().expect("populated slot");
4262        assert!(std::ptr::eq(a.as_ptr(), b.as_ptr()));
4263        assert_eq!(a.len(), b.len());
4264    }
4265
4266    #[test]
4267    fn observed_pid_matches_pre_lift_reconciler_chain_shape() {
4268        // Byte-identical parity pin between the borrow-form
4269        // primitive here and the pre-lift `tatara-reconciler
4270        // ::phase_machine` 3-line chain shape. Sweeps every corner
4271        // every callsite plausibly encounters (missing status,
4272        // empty pid slot, populated pid slot). A regression that
4273        // inserted a normalization step at the primitive the pre-
4274        // lift chain does NOT apply — or vice versa — surfaces
4275        // here rather than as silent drift between the pre-lift
4276        // consumer sites and the ONE substrate owner they now
4277        // route through. Peer to
4278        // `observed_flux_resources_matches_pre_lift_reconciler_chain_shape`
4279        // on the flux-resources axis's borrow-form primitive.
4280        fn pre_lift(p: &Process) -> Option<String> {
4281            p.status.as_ref().and_then(|s| s.pid.clone())
4282        }
4283        // Missing status.
4284        let mut p = Process::new("api", empty_spec());
4285        p.status = None;
4286        assert_eq!(p.observed_pid().map(str::to_string), pre_lift(&p));
4287        // Populated status, empty pid slot.
4288        let p = process_with_pid(None);
4289        assert_eq!(p.observed_pid().map(str::to_string), pre_lift(&p));
4290        // Populated status, populated pid slot.
4291        let p = process_with_pid(Some("seph.1.7"));
4292        assert_eq!(p.observed_pid().map(str::to_string), pre_lift(&p));
4293    }
4294
4295    #[test]
4296    fn observed_pid_missing_status_and_empty_slot_collapse_to_the_same_option_shape() {
4297        // Cross-corner coherence pin: the missing-`status` corner
4298        // and the populated-empty-slot corner return `Option`s whose
4299        // `.is_none()` observations are IDENTICAL. A regression
4300        // that promoted the missing-`status` corner to returning a
4301        // typed error (via a signature change to `Result<_, _>`) —
4302        // or that widened the empty-slot corner to a synthetic
4303        // `Some("")` — would surface here rather than as silent
4304        // operator-facing divergence between a never-status-
4305        // written Process and a status-emptied Process on the
4306        // ALLOCATE-PID gate.
4307        let mut p_no_status = Process::new("api", empty_spec());
4308        p_no_status.status = None;
4309        let p_empty_slot = process_with_pid(None);
4310        assert_eq!(
4311            p_no_status.observed_pid().is_none(),
4312            p_empty_slot.observed_pid().is_none()
4313        );
4314        assert_eq!(
4315            p_no_status.observed_pid().is_some(),
4316            p_empty_slot.observed_pid().is_some()
4317        );
4318    }
4319
4320    #[test]
4321    fn observed_pid_preserves_hierarchical_pid_format() {
4322        // Format-preservation pin: the hierarchical PID path
4323        // (dotted-segment form `seph.1.7`, matching the ported
4324        // `convergence-controller/src/identity.rs` scheme) reaches
4325        // the caller with segments and separators byte-identical
4326        // to the persisted `String`. A regression that inserted a
4327        // canonicalization pass (a segment-count validator, a
4328        // separator swap `.` → `/`, a leading/trailing whitespace
4329        // trim) would silently misroute the SIGTERM cascade's
4330        // `spec.identity.parent == Some(pid)` comparator against
4331        // children whose `parent` field was authored in the ported
4332        // scheme's exact form.
4333        for pid in ["seph", "seph.1", "seph.1.7", "seph.1.7.42"] {
4334            let p = process_with_pid(Some(pid));
4335            assert_eq!(p.observed_pid(), Some(pid));
4336        }
4337    }
4338
4339    // ─── Process::observed_attestation substrate pins ─────────────────
4340    //
4341    // Pins the borrow-form status-projection primitive on the
4342    // attestation-chain axis that owns the 3-line
4343    // `.status.as_ref().and_then(|s| s.attestation.as_ref())` chain
4344    // the two hand-authored `tatara-reconciler` sites
4345    // (`phase_machine::advance_to_attested` ATTEST composer +
4346    // `render::render_export_jobs` export-Job builder) restated by
4347    // hand pre-lift. Peer to the sibling `observed_pid_*` +
4348    // `observed_flux_resources_*` pin families; all three compose
4349    // the missing-`status` fallback + borrow-form return-shape
4350    // skeleton on distinct `ProcessStatus` slots. Fail-before-pass-
4351    // after granularity: `observed_attestation` did not exist
4352    // pre-lift, so any test invoking it fails to compile pre-lift
4353    // and passes post-lift.
4354
4355    fn sample_attestation(artifact: &str, intent: &str) -> ProcessAttestation {
4356        // Distinct pillar strings so a regression that swapped the
4357        // artifact / intent pillars silently surfaces as an
4358        // equality failure at the composed-root parity pin.
4359        ProcessAttestation::initial(artifact.to_string(), None, intent.to_string())
4360    }
4361
4362    fn process_with_attestation(attestation: Option<ProcessAttestation>) -> Process {
4363        let mut p = Process::new("api-gateway", empty_spec());
4364        p.metadata.namespace = Some("prod".into());
4365        let mut status = ProcessStatus::default();
4366        status.attestation = attestation;
4367        p.status = Some(status);
4368        p
4369    }
4370
4371    #[test]
4372    fn observed_attestation_returns_none_when_status_is_none() {
4373        // Missing-`status` corner pin: the primitive collapses the
4374        // no-status case to `None` so downstream `.is_some()` /
4375        // `if let Some(_)` / `.map(...)` behave identically on a
4376        // `Process` whose status field is `None` and on one whose
4377        // status carries an unpopulated `attestation` slot.
4378        // Matches the pre-lift `.and_then(...)` chain's `None`
4379        // byte-identically at every reconciler consumer's
4380        // downstream shape.
4381        let mut p = Process::new("api", empty_spec());
4382        p.status = None;
4383        assert!(p.observed_attestation().is_none());
4384    }
4385
4386    #[test]
4387    fn observed_attestation_returns_none_when_attestation_slot_is_none() {
4388        // Empty-slot-under-populated-status corner pin: the
4389        // primitive returns `None`, matching the missing-`status`
4390        // corner byte-identically. A regression that treated the
4391        // two corners differently (a `None`-vs-`Some(_)` signal
4392        // that downstream consumers could grep on) would silently
4393        // promote an internal representation detail (whether the
4394        // reconciler has ever written a status subresource) into
4395        // observable behavior at the ATTEST composer's
4396        // seed-vs-chain branch.
4397        let p = process_with_attestation(None);
4398        assert!(p.observed_attestation().is_none());
4399    }
4400
4401    #[test]
4402    fn observed_attestation_returns_borrow_when_slot_is_populated() {
4403        // Happy-path pin: with a populated `status.attestation`
4404        // slot, the primitive returns a borrowed
4405        // `&ProcessAttestation` whose fields match the persisted
4406        // record. A regression that filtered / reshaped /
4407        // canonicalized the record would surface here rather than
4408        // as silent skew at the downstream `prior.next(pillars)`
4409        // chain composer + the ephemeral-export receipt's
4410        // `previous_root` linker.
4411        let att = sample_attestation("art-1", "int-1");
4412        let composed_root = att.composed_root.clone();
4413        let p = process_with_attestation(Some(att));
4414        let observed = p.observed_attestation().expect("populated slot");
4415        assert_eq!(observed.artifact_hash, "art-1");
4416        assert_eq!(observed.intent_hash, "int-1");
4417        assert_eq!(observed.composed_root, composed_root);
4418        assert_eq!(observed.generation, 0);
4419        assert!(observed.previous_root.is_none());
4420    }
4421
4422    #[test]
4423    fn observed_attestation_is_a_zero_copy_borrow_projection() {
4424        // Borrow-discipline pin: the returned reference points at
4425        // the persisted `ProcessAttestation` in place — NOT a fresh
4426        // allocation or a clone. A regression that switched the
4427        // projection to an owned `ProcessAttestation` (via
4428        // `.clone()`) would defeat the zero-copy contract the
4429        // lift's primary strict-widening delivers (the pre-lift
4430        // 3-line chain returned a borrow, but the export-Job
4431        // builder then cloned `composed_root` off it; the post-
4432        // lift primitive preserves the borrow all the way to the
4433        // consumer's own cloning choice). Peer to the sibling
4434        // `observed_pid_is_a_zero_copy_borrow_projection` +
4435        // `observed_flux_resources_is_a_zero_copy_borrow_projection`
4436        // pins on the PID + flux-resources borrow-projection axes.
4437        let att = sample_attestation("art-1", "int-1");
4438        let p = process_with_attestation(Some(att));
4439        let observed = p.observed_attestation().expect("populated slot") as *const _;
4440        let persisted = p.status.as_ref().unwrap().attestation.as_ref().unwrap() as *const _;
4441        assert!(std::ptr::eq(observed, persisted));
4442    }
4443
4444    #[test]
4445    fn observed_attestation_is_a_pure_projection() {
4446        // Purity pin: calling the projection twice on the same
4447        // `Process` returns byte-identical borrows (same pointer).
4448        // A regression that introduced state — a lazy-cached
4449        // reference materialized on first call, a normalization
4450        // step that ran once and cached — would surface here
4451        // rather than as silent drift between the ATTEST composer
4452        // and the ephemeral-export receipt chain on the SAME
4453        // `Process` within one reconcile pass.
4454        let att = sample_attestation("art-1", "int-1");
4455        let p = process_with_attestation(Some(att));
4456        let a = p.observed_attestation().expect("populated slot") as *const _;
4457        let b = p.observed_attestation().expect("populated slot") as *const _;
4458        assert!(std::ptr::eq(a, b));
4459    }
4460
4461    #[test]
4462    fn observed_attestation_matches_pre_lift_reconciler_chain_shape() {
4463        // Byte-identical parity pin between the borrow-form
4464        // primitive here and the pre-lift `tatara-reconciler`
4465        // 3-line chain shape. Sweeps every corner every callsite
4466        // plausibly encounters (missing status, empty attestation
4467        // slot, populated attestation slot). A regression that
4468        // inserted a normalization step at the primitive the pre-
4469        // lift chain does NOT apply — or vice versa — surfaces
4470        // here rather than as silent drift between the pre-lift
4471        // consumer sites and the ONE substrate owner they now
4472        // route through. Peer to
4473        // `observed_pid_matches_pre_lift_reconciler_chain_shape` +
4474        // `observed_flux_resources_matches_pre_lift_reconciler_chain_shape`
4475        // on the PID + flux-resources axes.
4476        // `ProcessAttestation` does not derive `PartialEq` — the
4477        // parity check walks the `composed_root` field (the
4478        // byte-string every downstream consumer keys off) so a
4479        // regression that reshaped the record without touching
4480        // the composed-root observation surfaces here through
4481        // the receipt-chain projection.
4482        fn pre_lift(p: &Process) -> Option<String> {
4483            p.status
4484                .as_ref()
4485                .and_then(|s| s.attestation.as_ref())
4486                .map(|a| a.composed_root.clone())
4487        }
4488        // Missing status.
4489        let mut p = Process::new("api", empty_spec());
4490        p.status = None;
4491        assert_eq!(
4492            p.observed_attestation().map(|a| a.composed_root.clone()),
4493            pre_lift(&p)
4494        );
4495        // Populated status, empty attestation slot.
4496        let p = process_with_attestation(None);
4497        assert_eq!(
4498            p.observed_attestation().map(|a| a.composed_root.clone()),
4499            pre_lift(&p)
4500        );
4501        // Populated status, populated attestation slot.
4502        let p = process_with_attestation(Some(sample_attestation("art-1", "int-1")));
4503        assert_eq!(
4504            p.observed_attestation().map(|a| a.composed_root.clone()),
4505            pre_lift(&p)
4506        );
4507    }
4508
4509    #[test]
4510    fn observed_attestation_missing_status_and_empty_slot_collapse_to_the_same_option_shape() {
4511        // Cross-corner coherence pin: the missing-`status` corner
4512        // and the populated-empty-slot corner return `Option`s
4513        // whose `.is_none()` observations are IDENTICAL. A
4514        // regression that promoted the missing-`status` corner to
4515        // returning a typed error (via a signature change to
4516        // `Result<_, _>`) — or that widened the empty-slot corner
4517        // to a synthetic `Some(default_attestation)` — would
4518        // surface here rather than as silent operator-facing
4519        // divergence between a never-status-written Process and
4520        // an attestation-emptied Process on the ATTEST composer's
4521        // seed-vs-chain branch.
4522        let mut p_no_status = Process::new("api", empty_spec());
4523        p_no_status.status = None;
4524        let p_empty_slot = process_with_attestation(None);
4525        assert_eq!(
4526            p_no_status.observed_attestation().is_none(),
4527            p_empty_slot.observed_attestation().is_none()
4528        );
4529        assert_eq!(
4530            p_no_status.observed_attestation().is_some(),
4531            p_empty_slot.observed_attestation().is_some()
4532        );
4533    }
4534
4535    #[test]
4536    fn observed_attestation_preserves_chain_generation_field() {
4537        // Generation-preservation pin: a chained attestation
4538        // (`prior.next(...)` at generation N ≥ 1 with a
4539        // `previous_root` linked to `prior.composed_root`) reaches
4540        // the caller with its `generation` counter + `previous_root`
4541        // link byte-identical to the persisted record. The pre-lift
4542        // ATTEST composer discriminated exactly on this borrow's
4543        // `Some(prior)` vs `None` arm; a regression that dropped
4544        // the chain's `generation` counter (say, by folding
4545        // `next(...)` into a fresh `initial(...)` on every
4546        // reconcile pass) would silently reset every chain and
4547        // orphan every downstream `previous_root` link, but that
4548        // drift is invisible to a Process CRD reader who only
4549        // observes the LATEST composed_root.
4550        let prior = sample_attestation("art-0", "int-0");
4551        let chained = prior.next("art-1".to_string(), None, "int-1".to_string());
4552        let expected_generation = chained.generation;
4553        let expected_previous = chained.previous_root.clone();
4554        let p = process_with_attestation(Some(chained));
4555        let observed = p.observed_attestation().expect("populated slot");
4556        assert_eq!(observed.generation, expected_generation);
4557        assert_eq!(observed.generation, 1);
4558        assert_eq!(observed.previous_root, expected_previous);
4559        assert_eq!(
4560            observed.previous_root.as_deref(),
4561            Some(prior.composed_root.as_str())
4562        );
4563    }
4564
4565    // ─── Process::observed_identity substrate pins ────────────────────
4566    //
4567    // The borrow-form status-projection primitive on the resolved-
4568    // identity axis. Collapses the paired 3-line `.status.as_ref()
4569    // .and_then(|s| s.identity.<clone|as_ref>())` chain every
4570    // consumer in `tatara-reconciler` restated by hand pre-lift at
4571    // TWO sites (`phase_machine::handle_forking` seed +
4572    // `ssapply::inject_annotations` content-hash annotation
4573    // composer). Peer to the sibling `observed_pid_*` +
4574    // `observed_attestation_*` + `observed_flux_resources_*` pin
4575    // families; all four compose the same missing-`status` fallback
4576    // + borrow-form return-shape skeleton on distinct
4577    // `ProcessStatus` slots. Each pin fails-before-pass-after
4578    // granularity: `observed_identity` did not exist pre-lift, so
4579    // any test invoking it fails to compile pre-lift and passes
4580    // post-lift.
4581
4582    fn sample_identity(name: &str) -> Identity {
4583        // Distinct name + content_hash + override flag so a
4584        // regression that reshaped one slot surfaces at the
4585        // populated-slot pin's field-equality check without
4586        // aliasing the sibling slots.
4587        Identity {
4588            name: name.to_string(),
4589            content_hash: "a".repeat(26),
4590            name_override: true,
4591        }
4592    }
4593
4594    fn process_with_identity(identity: Option<Identity>) -> Process {
4595        let mut p = Process::new("api-gateway", empty_spec());
4596        p.metadata.namespace = Some("prod".into());
4597        let mut status = ProcessStatus::default();
4598        status.identity = identity;
4599        p.status = Some(status);
4600        p
4601    }
4602
4603    #[test]
4604    fn observed_identity_returns_none_when_status_is_none() {
4605        // Missing-`status` corner pin: the primitive collapses the
4606        // no-status case to `None` so downstream `.is_some()` /
4607        // `if let Some(_)` / `.cloned().unwrap_or_else(...)` behave
4608        // identically on a `Process` whose status field is `None`
4609        // and on one whose status carries an unpopulated `identity`
4610        // slot. Matches the pre-lift `.and_then(...)` chain's `None`
4611        // byte-identically at every reconciler consumer's
4612        // downstream shape.
4613        let mut p = Process::new("api", empty_spec());
4614        p.status = None;
4615        assert!(p.observed_identity().is_none());
4616    }
4617
4618    #[test]
4619    fn observed_identity_returns_none_when_identity_slot_is_none() {
4620        // Empty-slot-under-populated-status corner pin: the
4621        // primitive returns `None`, matching the missing-`status`
4622        // corner byte-identically. A regression that treated the
4623        // two corners differently (a `None`-vs-`Some(_)` signal
4624        // that downstream consumers could grep on) would silently
4625        // promote an internal representation detail (whether the
4626        // reconciler has ever written a status subresource) into
4627        // observable behavior at the FORK-time `derive_identity`
4628        // fallback branch.
4629        let p = process_with_identity(None);
4630        assert!(p.observed_identity().is_none());
4631    }
4632
4633    #[test]
4634    fn observed_identity_returns_borrow_when_slot_is_populated() {
4635        // Happy-path pin: with a populated `status.identity` slot,
4636        // the primitive returns a borrowed `&Identity` whose fields
4637        // match the persisted record. A regression that filtered /
4638        // reshaped / canonicalized the record would surface here
4639        // rather than as silent skew at the FORK-time seed's
4640        // `.cloned().unwrap_or_else(derive_identity)` composition
4641        // + the SSA-time content-hash annotation stamp on the SAME
4642        // Process.
4643        let id = sample_identity("seph");
4644        let expected = id.clone();
4645        let p = process_with_identity(Some(id));
4646        let observed = p.observed_identity().expect("populated slot");
4647        assert_eq!(observed, &expected);
4648        assert_eq!(observed.name, "seph");
4649        assert_eq!(observed.content_hash, "a".repeat(26));
4650        assert!(observed.name_override);
4651    }
4652
4653    #[test]
4654    fn observed_identity_is_a_zero_copy_borrow_projection() {
4655        // Borrow-discipline pin: the returned reference points at
4656        // the persisted `Identity` in place — NOT a fresh
4657        // allocation or a clone. A regression that switched the
4658        // projection to an owned `Identity` (via `.clone()`) would
4659        // defeat the zero-copy contract the lift's primary strict-
4660        // widening delivers (the SSA-time consumer never clones the
4661        // whole `Identity`, only the `content_hash` field it stamps
4662        // onto the annotation map, so the borrow-form return
4663        // shape's happy-path allocation count is exactly ZERO).
4664        // Peer to the sibling
4665        // `observed_attestation_is_a_zero_copy_borrow_projection`
4666        // + `observed_pid_is_a_zero_copy_borrow_projection` +
4667        // `observed_flux_resources_is_a_zero_copy_borrow_projection`
4668        // pins on the attestation-chain + PID + flux-resources
4669        // borrow-projection axes.
4670        let id = sample_identity("seph");
4671        let p = process_with_identity(Some(id));
4672        let observed = p.observed_identity().expect("populated slot") as *const _;
4673        let persisted = p.status.as_ref().unwrap().identity.as_ref().unwrap() as *const _;
4674        assert!(std::ptr::eq(observed, persisted));
4675    }
4676
4677    #[test]
4678    fn observed_identity_is_a_pure_projection() {
4679        // Purity pin: calling the projection twice on the same
4680        // `Process` returns byte-identical borrows (same pointer).
4681        // A regression that introduced state — a lazy-cached
4682        // reference materialized on first call, a normalization
4683        // step that ran once and cached — would surface here
4684        // rather than as silent drift between the FORK-time
4685        // identity seed and the SSA-time content-hash annotation
4686        // stamp on the SAME `Process` within one reconcile pass.
4687        let p = process_with_identity(Some(sample_identity("seph")));
4688        let a = p.observed_identity().expect("populated slot") as *const _;
4689        let b = p.observed_identity().expect("populated slot") as *const _;
4690        assert!(std::ptr::eq(a, b));
4691    }
4692
4693    #[test]
4694    fn observed_identity_matches_pre_lift_reconciler_chain_shape() {
4695        // Byte-identical parity pin between the borrow-form
4696        // primitive here and the pre-lift `tatara-reconciler`
4697        // 3-line chain shape. Sweeps every corner every callsite
4698        // plausibly encounters (missing status, empty identity
4699        // slot, populated identity slot). A regression that
4700        // inserted a normalization step at the primitive the pre-
4701        // lift chain does NOT apply — or vice versa — surfaces
4702        // here rather than as silent drift between the pre-lift
4703        // consumer sites and the ONE substrate owner they now
4704        // route through. Peer to
4705        // `observed_attestation_matches_pre_lift_reconciler_chain_shape`
4706        // + `observed_pid_matches_pre_lift_reconciler_chain_shape`
4707        // + `observed_flux_resources_matches_pre_lift_reconciler_chain_shape`
4708        // on the attestation-chain + PID + flux-resources axes.
4709        fn pre_lift(p: &Process) -> Option<Identity> {
4710            p.status.as_ref().and_then(|s| s.identity.clone())
4711        }
4712        // Missing status.
4713        let mut p = Process::new("api", empty_spec());
4714        p.status = None;
4715        assert_eq!(p.observed_identity().cloned(), pre_lift(&p));
4716        // Populated status, empty identity slot.
4717        let p = process_with_identity(None);
4718        assert_eq!(p.observed_identity().cloned(), pre_lift(&p));
4719        // Populated status, populated identity slot.
4720        let p = process_with_identity(Some(sample_identity("seph")));
4721        assert_eq!(p.observed_identity().cloned(), pre_lift(&p));
4722    }
4723
4724    #[test]
4725    fn observed_identity_missing_status_and_empty_slot_collapse_to_the_same_option_shape() {
4726        // Cross-corner coherence pin: the missing-`status` corner
4727        // and the populated-empty-slot corner return `Option`s
4728        // whose `.is_none()` observations are IDENTICAL. A
4729        // regression that promoted the missing-`status` corner to
4730        // returning a typed error (via a signature change to
4731        // `Result<_, _>`) — or that widened the empty-slot corner
4732        // to a synthetic `Some(derive_identity(default_spec))` —
4733        // would surface here rather than as silent operator-facing
4734        // divergence between a never-status-written Process and an
4735        // identity-cleared Process on the FORK-time seed branch.
4736        let mut p_no_status = Process::new("api", empty_spec());
4737        p_no_status.status = None;
4738        let p_empty_slot = process_with_identity(None);
4739        assert_eq!(
4740            p_no_status.observed_identity().is_none(),
4741            p_empty_slot.observed_identity().is_none()
4742        );
4743        assert_eq!(
4744            p_no_status.observed_identity().is_some(),
4745            p_empty_slot.observed_identity().is_some()
4746        );
4747    }
4748
4749    #[test]
4750    fn observed_identity_cloned_composes_with_derive_identity_fallback() {
4751        // Cross-primitive composition pin: the borrow-form
4752        // primitive threaded through `.cloned().unwrap_or_else(||
4753        // derive_identity(...))` reproduces the pre-lift FORK-time
4754        // seed's owned-`Identity` shape at every corner. Binds the
4755        // exact composition the `phase_machine::handle_forking`
4756        // consumer performs: on the populated-slot corner the
4757        // reconciler-persisted `Identity` is returned verbatim (the
4758        // fallback never fires), and on both empty corners
4759        // (missing-status + empty-slot) the fallback fires
4760        // producing a fresh `derive_identity(&spec,
4761        // name_override)`. A regression that (a) swapped the
4762        // fallback direction, (b) made `.cloned()` re-derive
4763        // instead of clone, or (c) made the empty-slot corner
4764        // return a synthetic `Some(default_identity)` collides
4765        // with the fallback surfaces here rather than as silent
4766        // FORK-time PID allocator skew.
4767        let spec = empty_spec();
4768        let fallback_expected = crate::identity::derive_identity(&spec, None);
4769        // Populated-slot corner: the seed returns the persisted
4770        // identity, NOT the derive fallback.
4771        let persisted = sample_identity("seph");
4772        let p = process_with_identity(Some(persisted.clone()));
4773        let seed = p.observed_identity().cloned().unwrap_or_else(|| {
4774            crate::identity::derive_identity(&p.spec, p.declared_name_override())
4775        });
4776        assert_eq!(seed, persisted);
4777        assert_ne!(seed, fallback_expected);
4778        // Empty-slot corner: the seed fires the derive fallback.
4779        let p = process_with_identity(None);
4780        let seed = p.observed_identity().cloned().unwrap_or_else(|| {
4781            crate::identity::derive_identity(&p.spec, p.declared_name_override())
4782        });
4783        assert_eq!(seed, fallback_expected);
4784        // Missing-status corner: the seed fires the derive
4785        // fallback, byte-identical to the empty-slot corner.
4786        let mut p = Process::new("api-gateway", empty_spec());
4787        p.metadata.namespace = Some("prod".into());
4788        p.status = None;
4789        let seed = p.observed_identity().cloned().unwrap_or_else(|| {
4790            crate::identity::derive_identity(&p.spec, p.declared_name_override())
4791        });
4792        assert_eq!(seed, fallback_expected);
4793    }
4794
4795    // ─── Process::observed_phase substrate pins ───────────────────────
4796    //
4797    // The copy-form status-projection primitive on the phase axis.
4798    // Collapses the paired 3-line `.status.as_ref().map(|s| s.phase)`
4799    // chain every consumer in `tatara-reconciler` restated by hand
4800    // pre-lift at FIVE sites. Peer to the borrow-form
4801    // `observed_pid_*` + `observed_flux_resources_*` +
4802    // `observed_attestation_*` pin families; all four compose the
4803    // same missing-`status` fallback skeleton on distinct
4804    // `ProcessStatus` slots, with the phase-axis form returning
4805    // `Option<ProcessPhase>` (copy of a `Copy` scalar) rather than
4806    // `Option<&T>` (borrow) because the underlying slot is a bare
4807    // `ProcessPhase` — no allocation to borrow past, and the enum
4808    // is one byte on the wire. Each pin fails-before-pass-after
4809    // granularity: `observed_phase` did not exist pre-lift, so any
4810    // test invoking it fails to compile pre-lift and passes
4811    // post-lift.
4812
4813    fn process_with_phase(phase: Option<ProcessPhase>) -> Process {
4814        let mut p = Process::new("api-gateway", empty_spec());
4815        p.metadata.namespace = Some("prod".into());
4816        if let Some(ph) = phase {
4817            let mut status = ProcessStatus::default();
4818            status.phase = ph;
4819            p.status = Some(status);
4820        }
4821        p
4822    }
4823
4824    #[test]
4825    fn observed_phase_returns_none_when_status_is_none() {
4826        // Missing-`status` corner pin: the primitive collapses the
4827        // no-status case to `None` so downstream `.unwrap_or(...)`
4828        // at every reconciler consumer chooses the default
4829        // deliberately (`Pending` for the top-level dispatch seed
4830        // + boundary evaluator + routing groupby; `Attested` for
4831        // the released-from annotation composer). Matches the
4832        // pre-lift `.map(|s| s.phase)` chain's `None`
4833        // byte-identically at every consumer's downstream shape.
4834        let mut p = Process::new("api", empty_spec());
4835        p.status = None;
4836        assert!(p.observed_phase().is_none());
4837    }
4838
4839    #[test]
4840    fn observed_phase_returns_some_default_when_status_is_populated_with_default_phase() {
4841        // Populated-status corner pin: the primitive returns
4842        // `Some(ProcessPhase::default())` — a `ProcessStatus`
4843        // constructed via `default()` carries `phase: Pending`
4844        // because the phase field is a bare `ProcessPhase` (not
4845        // `Option<ProcessPhase>`), so there is NO "empty slot"
4846        // corner peer to the borrow-form projections' empty-slot
4847        // pins. A regression that reshaped the return type to
4848        // filter out `Pending` (treating it as "unset") would
4849        // surface here and silently break the top-level
4850        // dispatcher's Pending → Forking transition on a Process
4851        // freshly written by the reconciler.
4852        let p = process_with_phase(Some(ProcessPhase::default()));
4853        assert_eq!(p.observed_phase(), Some(ProcessPhase::Pending));
4854        assert_eq!(p.observed_phase(), Some(ProcessPhase::default()));
4855    }
4856
4857    #[test]
4858    fn observed_phase_returns_persisted_phase_when_status_is_populated() {
4859        // Happy-path pin: with a populated `status.phase` slot,
4860        // the primitive returns the persisted `ProcessPhase`.
4861        // A regression that filtered / reshaped / canonicalized
4862        // the phase would surface here rather than as silent
4863        // skew at the top-level dispatcher's phase handler
4864        // dispatch on the SAME Process.
4865        let p = process_with_phase(Some(ProcessPhase::Running));
4866        assert_eq!(p.observed_phase(), Some(ProcessPhase::Running));
4867    }
4868
4869    #[test]
4870    fn observed_phase_is_a_pure_projection() {
4871        // Purity pin: two consecutive calls return byte-identical
4872        // `Option<ProcessPhase>` values (no lazy materialization,
4873        // no interior mutation of `self`). Peer to the sibling
4874        // `observed_pid_is_a_pure_projection` +
4875        // `observed_flux_resources_is_a_pure_projection` +
4876        // `observed_attestation_is_a_pure_projection` pins; all
4877        // four bind the pure-projection discipline on the ONE
4878        // substrate accessor per status slot.
4879        let p = process_with_phase(Some(ProcessPhase::Attested));
4880        let a = p.observed_phase();
4881        let b = p.observed_phase();
4882        assert_eq!(a, b);
4883        assert_eq!(a, Some(ProcessPhase::Attested));
4884    }
4885
4886    #[test]
4887    fn observed_phase_matches_pre_lift_reconciler_chain_shape() {
4888        // Parity pin: sweeps the two corners every pre-lift
4889        // consumer plausibly encountered (missing status,
4890        // populated status with a particular phase) and compares
4891        // the substrate call against a hand-authored pre-lift
4892        // chain byte-identically. A regression that reshaped ANY
4893        // of the two corners would surface here rather than as
4894        // silent operator-facing skew between the top-level
4895        // dispatcher and any of the four other reconciler
4896        // consumers on the SAME `Process`.
4897        fn pre_lift(p: &Process) -> Option<ProcessPhase> {
4898            p.status.as_ref().map(|s| s.phase)
4899        }
4900        let mut p = Process::new("api", empty_spec());
4901        p.status = None;
4902        assert_eq!(p.observed_phase(), pre_lift(&p));
4903        let p = process_with_phase(Some(ProcessPhase::Running));
4904        assert_eq!(p.observed_phase(), pre_lift(&p));
4905        let p = process_with_phase(Some(ProcessPhase::Attested));
4906        assert_eq!(p.observed_phase(), pre_lift(&p));
4907        let p = process_with_phase(Some(ProcessPhase::Failed));
4908        assert_eq!(p.observed_phase(), pre_lift(&p));
4909    }
4910
4911    #[test]
4912    fn observed_phase_default_unwrap_matches_pre_lift_pending_default() {
4913        // Callsite-shape pin: three of the FIVE pre-lift consumers
4914        // (`controller::reconcile`, `boundary::evaluate_process_phase`,
4915        // `table_controller::stable_name_group_key`) closed the
4916        // 3-line chain with `.unwrap_or(ProcessPhase::Pending)`
4917        // (identical to `.unwrap_or_default()`). This pin binds
4918        // that call-site shape: `observed_phase().unwrap_or
4919        // (Pending)` returns `Pending` on missing status and the
4920        // persisted phase otherwise. A regression that swapped
4921        // the `None` sentinel's downstream default would surface
4922        // here rather than as silent skew at three of the five
4923        // consumer sites.
4924        let mut p = Process::new("api", empty_spec());
4925        p.status = None;
4926        assert_eq!(
4927            p.observed_phase().unwrap_or(ProcessPhase::Pending),
4928            ProcessPhase::Pending
4929        );
4930        let p = process_with_phase(Some(ProcessPhase::Running));
4931        assert_eq!(
4932            p.observed_phase().unwrap_or(ProcessPhase::Pending),
4933            ProcessPhase::Running
4934        );
4935    }
4936
4937    #[test]
4938    fn observed_phase_attested_unwrap_matches_pre_lift_released_from_default() {
4939        // Callsite-shape pin: the ONE pre-lift consumer
4940        // (`phase_machine::p_current_phase_str` — the
4941        // released-from annotation composer) closed the 3-line
4942        // chain with `.unwrap_or(ProcessPhase::Attested)` rather
4943        // than the `Default` (`Pending`). This pin binds that
4944        // call-site shape: `observed_phase().unwrap_or(Attested)`
4945        // returns `Attested` on missing status and the persisted
4946        // phase otherwise. A regression that folded the
4947        // `Attested`-default consumer into the `Pending`-default
4948        // majority would break the SIGSTOP/SIGCONT release gate's
4949        // "which annotation label to emit" branch — the pin binds
4950        // the primitive at the raw `Option<ProcessPhase>` form so
4951        // this default choice stays local at the callsite.
4952        let mut p = Process::new("api", empty_spec());
4953        p.status = None;
4954        assert_eq!(
4955            p.observed_phase().unwrap_or(ProcessPhase::Attested),
4956            ProcessPhase::Attested
4957        );
4958        let p = process_with_phase(Some(ProcessPhase::Failed));
4959        assert_eq!(
4960            p.observed_phase().unwrap_or(ProcessPhase::Attested),
4961            ProcessPhase::Failed
4962        );
4963    }
4964
4965    #[test]
4966    fn observed_phase_preserves_every_process_phase_variant() {
4967        // Round-trip pin: every `ProcessPhase` variant round-
4968        // trips through the primitive unchanged. Peer to the
4969        // sibling `observed_pid_preserves_hierarchical_pid_format`
4970        // pin's dotted-segment sweep; this pin sweeps the closed
4971        // set of `ProcessPhase` variants directly so a
4972        // canonicalization pass that dropped or reshaped one
4973        // (e.g. folded `Reconverging` back into `Execing`, or
4974        // remapped `Zombie` to `Reaped`) surfaces here rather
4975        // than as silent skew at the SIGSTOP/SIGCONT release
4976        // gate's phase-name annotation branch. Covers every
4977        // variant the `ProcessPhase::DeriveClosedSet` enumerates
4978        // so a future variant addition surfaces via the closed-
4979        // set macro rather than at a silent partial sweep.
4980        for phase in [
4981            ProcessPhase::Pending,
4982            ProcessPhase::Forking,
4983            ProcessPhase::Execing,
4984            ProcessPhase::Running,
4985            ProcessPhase::Attested,
4986            ProcessPhase::Reconverging,
4987            ProcessPhase::Releasing,
4988            ProcessPhase::Exiting,
4989            ProcessPhase::Failed,
4990            ProcessPhase::Zombie,
4991            ProcessPhase::Reaped,
4992        ] {
4993            let p = process_with_phase(Some(phase));
4994            assert_eq!(
4995                p.observed_phase(),
4996                Some(phase),
4997                "phase variant {phase:?} did not round-trip"
4998            );
4999        }
5000    }
5001
5002    // ─── Process::observed_phase_or_pending substrate pins ─────────────
5003    //
5004    // Pins the copy-form status-projection primitive on the phase
5005    // axis with the `Pending` sink applied. Sibling to the raw
5006    // `observed_phase_*` pin family on the (return-form × fallback
5007    // shape) axis pair — the raw-`Option` corner stays with the
5008    // sibling family; this pin family opens the `Pending`-defaulted
5009    // corner that four of the five pre-lift `observed_phase`
5010    // consumers wrote by hand. Fail-before-pass-after granularity:
5011    // `observed_phase_or_pending` did not exist pre-lift, so any
5012    // test invoking it fails to compile pre-lift and passes
5013    // post-lift.
5014
5015    #[test]
5016    fn observed_phase_or_pending_returns_pending_when_status_is_none() {
5017        // Missing-`status` corner pin: the primitive collapses the
5018        // no-status case to `Pending` — the sink four of the five
5019        // pre-lift `observed_phase` consumers wrote by hand
5020        // (`controller::reconcile` / `boundary::
5021        // evaluate_process_phase` / `table_controller::
5022        // stable_name_group_key` / `controller_pool::reconcile_pool`)
5023        // and the sentinel `ProcessPhase::default()` returns. A
5024        // regression that folded the `None` sink to any other phase
5025        // (e.g. `Forking` — treating "not yet observed" as "already
5026        // dispatched") would silently mis-seed the top-level
5027        // dispatcher's `Pending → Forking` transition and surface as
5028        // operator-visible reconcile-cycle skew on a freshly-forked
5029        // Process rather than at this pin.
5030        let mut p = Process::new("api", empty_spec());
5031        p.status = None;
5032        assert_eq!(p.observed_phase_or_pending(), ProcessPhase::Pending);
5033    }
5034
5035    #[test]
5036    fn observed_phase_or_pending_returns_persisted_phase_when_status_is_populated() {
5037        // Populated-status corner pin: the primitive passes through
5038        // the persisted `ProcessPhase` unchanged — the sink only
5039        // fires on missing `status`, not on a populated one carrying
5040        // a `Pending`-adjacent variant. Two variants pinned to
5041        // separate the "pass through the persisted phase" arm from
5042        // the "sink fires" arm: `Running` (mid-lifecycle) and
5043        // `Attested` (post-verify) both round-trip unchanged where
5044        // a regression that always returned `Pending` (dropped the
5045        // pass-through arm entirely) would surface here rather than
5046        // as silent skew at every reconciler's per-phase branch.
5047        let p = process_with_phase(Some(ProcessPhase::Running));
5048        assert_eq!(p.observed_phase_or_pending(), ProcessPhase::Running);
5049        let p = process_with_phase(Some(ProcessPhase::Attested));
5050        assert_eq!(p.observed_phase_or_pending(), ProcessPhase::Attested);
5051    }
5052
5053    #[test]
5054    fn observed_phase_or_pending_matches_pre_lift_unwrap_or_pending_chain_shape() {
5055        // Byte-identical parity pin: the primitive's return equals
5056        // the pre-lift two-link `.observed_phase().unwrap_or
5057        // (ProcessPhase::Pending)` chain at every one of the four
5058        // corner values (missing `status` → `Pending`, populated
5059        // with `Pending` → `Pending`, populated with a mid-lifecycle
5060        // variant → pass-through, populated with a terminal variant
5061        // → pass-through). A regression that swapped the sink to
5062        // `ProcessPhase::default()` (currently equivalent to
5063        // `Pending`) would keep this pin green until the enum's
5064        // `Default` impl drifted — the explicit `Pending` spelling
5065        // in the pin binds the operator-visible label rather than
5066        // the derived `Default`, so a future rename or reordering
5067        // of `ProcessPhase` variants that shifted `Default` off
5068        // `Pending` would surface here rather than as silent skew
5069        // at the four downstream consumer sites.
5070        let pre_lift = |p: &Process| p.observed_phase().unwrap_or(ProcessPhase::Pending);
5071        let mut p = Process::new("api", empty_spec());
5072        p.status = None;
5073        assert_eq!(p.observed_phase_or_pending(), pre_lift(&p));
5074        let p = process_with_phase(Some(ProcessPhase::Pending));
5075        assert_eq!(p.observed_phase_or_pending(), pre_lift(&p));
5076        let p = process_with_phase(Some(ProcessPhase::Running));
5077        assert_eq!(p.observed_phase_or_pending(), pre_lift(&p));
5078        let p = process_with_phase(Some(ProcessPhase::Reaped));
5079        assert_eq!(p.observed_phase_or_pending(), pre_lift(&p));
5080    }
5081
5082    #[test]
5083    fn observed_phase_or_pending_is_a_pure_projection() {
5084        // Purity pin: two back-to-back calls on the same `Process`
5085        // return the same `ProcessPhase` — the primitive stamps no
5086        // side effect (no clock read, no metadata write, no
5087        // `status` mutation) despite the sibling `observed_phase`
5088        // taking `&self` too. Peer to the sibling `observed_phase`
5089        // purity pin; a regression that folded a clock read (e.g.
5090        // "if the sink fired, stamp `phase_since = Utc::now()`")
5091        // into the primitive would surface here rather than at the
5092        // consumer sites' downstream reconcile-cycle behavior.
5093        let p = process_with_phase(Some(ProcessPhase::Running));
5094        let a = p.observed_phase_or_pending();
5095        let b = p.observed_phase_or_pending();
5096        assert_eq!(a, b);
5097    }
5098
5099    #[test]
5100    fn observed_phase_or_pending_preserves_every_process_phase_variant() {
5101        // Round-trip pin: every `ProcessPhase` variant round-trips
5102        // through the primitive unchanged when the `status` slot is
5103        // populated. Peer to the sibling `observed_phase_preserves
5104        // _every_process_phase_variant` sweep; this pin sweeps the
5105        // closed set through the `Pending`-sinked accessor rather
5106        // than the raw-`Option` accessor so a canonicalization pass
5107        // that dropped or reshaped one variant (e.g. folded
5108        // `Reconverging` back into `Execing`, remapped `Zombie` to
5109        // `Reaped`) surfaces at BOTH primitives' pin sets rather
5110        // than as silent skew at a subset of the reconciler
5111        // consumers. Covers every variant the
5112        // `ProcessPhase::DeriveClosedSet` enumerates so a future
5113        // variant addition surfaces via the closed-set macro rather
5114        // than at a silent partial sweep.
5115        for phase in [
5116            ProcessPhase::Pending,
5117            ProcessPhase::Forking,
5118            ProcessPhase::Execing,
5119            ProcessPhase::Running,
5120            ProcessPhase::Attested,
5121            ProcessPhase::Reconverging,
5122            ProcessPhase::Releasing,
5123            ProcessPhase::Exiting,
5124            ProcessPhase::Failed,
5125            ProcessPhase::Zombie,
5126            ProcessPhase::Reaped,
5127        ] {
5128            let p = process_with_phase(Some(phase));
5129            assert_eq!(
5130                p.observed_phase_or_pending(),
5131                phase,
5132                "phase variant {phase:?} did not round-trip through observed_phase_or_pending"
5133            );
5134        }
5135    }
5136
5137    // ─── Process::observed_phase_since substrate pins ──────────────────
5138    //
5139    // Pins the copy-form status-projection primitive on the
5140    // `status.phase_since` axis that owns the paired 5-line
5141    // `.status.as_ref().and_then(|s| s.phase_since).unwrap_or_else
5142    // (Utc::now)` chain the pool reconciler's per-owned-Process
5143    // `PoolMember { entered_state_at: … }` seed restated by hand pre-
5144    // lift. Peer to the sibling `observed_phase_*` +
5145    // `observed_identity_*` + `observed_attestation_*` +
5146    // `observed_flux_resources_*` + `observed_pid_*` + `created_at_*`
5147    // pin families — all six / seven primitives project a wire-format
5148    // `Option<T>` slot into a `Copy`-or-borrow inner value at ONE
5149    // owner. Fail-before-pass-after granularity: `observed_phase_since`
5150    // did not exist pre-lift, so any test invoking it fails to
5151    // compile pre-lift and passes post-lift.
5152
5153    fn process_with_phase_since(phase_since: Option<DateTime<Utc>>) -> Process {
5154        let mut p = Process::new("api-gateway", empty_spec());
5155        p.metadata.namespace = Some("prod".into());
5156        let mut status = ProcessStatus::default();
5157        status.phase_since = phase_since;
5158        p.status = Some(status);
5159        p
5160    }
5161
5162    #[test]
5163    fn observed_phase_since_returns_none_when_status_is_none() {
5164        // Missing-`status` corner pin: the primitive collapses the
5165        // no-status case to `None` so the pool reconciler's `PoolMember
5166        // { entered_state_at: p.observed_phase_since().unwrap_or_else
5167        // (Utc::now), .. }` seed synthesizes a "just entered" anchor
5168        // at its own tail rather than materializing a stale timestamp
5169        // at the substrate. Matches the pre-lift `.and_then(|s| s
5170        // .phase_since)` chain's `None` byte-identically at the
5171        // consumer's downstream tail.
5172        let mut p = Process::new("api", empty_spec());
5173        p.status = None;
5174        assert!(p.observed_phase_since().is_none());
5175    }
5176
5177    #[test]
5178    fn observed_phase_since_returns_none_when_slot_is_empty() {
5179        // Populated-status + empty-slot corner pin: a `ProcessStatus`
5180        // whose `phase_since` slot is `None` (a freshly-forked
5181        // Process whose reconciler has not yet stamped a first
5182        // transition) collapses to `None` at the primitive. The
5183        // paired-corner collapse with the missing-`status` corner
5184        // (both → `None`) matches what `.and_then` produces
5185        // structurally — one `None` cannot recover into a `Some` at
5186        // the flat outer wrapper. A regression that swapped the outer
5187        // combinator to `.map(|s| s.phase_since)` would flatten to
5188        // `Option<Option<_>>` and the compiler would reject the
5189        // signature, but a regression that "synthesized" a default
5190        // anchor at the substrate (e.g. `Utc::now()` on the empty
5191        // slot) would silently break the callsite's own
5192        // `.unwrap_or_else(Utc::now)` tail's semantics — the sink
5193        // fires ONCE at the callsite, not twice.
5194        let p = process_with_phase_since(None);
5195        assert!(p.observed_phase_since().is_none());
5196    }
5197
5198    #[test]
5199    fn observed_phase_since_returns_populated_timestamp_verbatim() {
5200        // Populated-slot corner pin: with a populated `status
5201        // .phase_since` slot, the primitive returns the persisted
5202        // `DateTime<Utc>` verbatim — no rounding, no timezone
5203        // stripping, no `Time` wrapper leaked. A regression that
5204        // canonicalized the timestamp (e.g. truncated to the second,
5205        // stripped the timezone marker) would surface here rather
5206        // than as silent skew at the pool reconciler's per-member
5207        // entered-state-at seed comparison against `Utc::now()`
5208        // downstream at `pool_phase_from_members`.
5209        let anchor = crate::time::seconds_ago(720);
5210        let p = process_with_phase_since(Some(anchor));
5211        assert_eq!(p.observed_phase_since(), Some(anchor));
5212    }
5213
5214    #[test]
5215    fn observed_phase_since_is_a_pure_projection() {
5216        // Purity pin: two consecutive calls return byte-identical
5217        // `Option<DateTime<Utc>>` values (no lazy materialization,
5218        // no interior mutation of `self`, no wall-clock read on the
5219        // empty corner). Peer to the sibling
5220        // `is_being_deleted_is_a_pure_projection` +
5221        // `created_at_is_a_pure_projection` +
5222        // `observed_phase_is_a_pure_projection` +
5223        // `observed_phase_or_pending_is_a_pure_projection` pins; all
5224        // five bind the pure-projection discipline on the ONE
5225        // substrate accessor per metadata / status slot. A
5226        // regression that folded the impure `Utc::now()` sink into
5227        // this primitive (rather than keeping it at the callsite's
5228        // `.unwrap_or_else(Utc::now)` tail alongside the sibling
5229        // `created_at` seed) would surface here as two consecutive
5230        // calls that returned distinct `Some(now_1)` /
5231        // `Some(now_2)` values.
5232        let anchor = crate::time::seconds_ago(5);
5233        let p = process_with_phase_since(Some(anchor));
5234        let a = p.observed_phase_since();
5235        let b = p.observed_phase_since();
5236        assert_eq!(a, b);
5237        assert_eq!(a, Some(anchor));
5238        // Empty-slot corner: pure `None`, not a fresh `Utc::now()`.
5239        let p_empty = process_with_phase_since(None);
5240        let a = p_empty.observed_phase_since();
5241        let b = p_empty.observed_phase_since();
5242        assert_eq!(a, b);
5243        assert!(a.is_none());
5244    }
5245
5246    #[test]
5247    fn observed_phase_since_matches_pre_lift_pool_reconciler_chain_shape() {
5248        // Byte-identical parity pin between the copy-form primitive
5249        // here and the pre-lift `tatara-pool-reconciler::
5250        // controller_pool::reconcile_inner` 5-line chain shape
5251        // (without the callsite's `.unwrap_or_else(Utc::now)` tail —
5252        // that tail stays at the callsite). Sweeps every corner
5253        // every pre-lift callsite plausibly encountered: missing
5254        // `status`, populated `status` + empty `phase_since` slot,
5255        // populated `status` + populated `phase_since` slot. A
5256        // regression that inserted a normalization step at the
5257        // primitive the pre-lift chain does NOT apply — or vice
5258        // versa — surfaces here rather than as silent drift between
5259        // the pre-lift consumer site and the ONE substrate owner it
5260        // now routes through.
5261        fn pre_lift(p: &Process) -> Option<DateTime<Utc>> {
5262            p.status.as_ref().and_then(|s| s.phase_since)
5263        }
5264        // Missing status.
5265        let mut p = Process::new("x", empty_spec());
5266        p.status = None;
5267        assert_eq!(p.observed_phase_since(), pre_lift(&p));
5268        // Populated status, empty slot.
5269        let p = process_with_phase_since(None);
5270        assert_eq!(p.observed_phase_since(), pre_lift(&p));
5271        // Populated status, populated slot.
5272        let anchor = crate::time::seconds_ago(90);
5273        let p = process_with_phase_since(Some(anchor));
5274        assert_eq!(p.observed_phase_since(), pre_lift(&p));
5275    }
5276
5277    #[test]
5278    fn observed_phase_since_missing_status_and_empty_slot_collapse_to_the_same_option_shape() {
5279        // Cross-corner coherence pin: the missing-`status` corner
5280        // AND the populated-empty-slot corner return `Option`s
5281        // whose `.is_none()` observations are IDENTICAL — a
5282        // shape peer to `observed_identity_missing_status_and_empty
5283        // _slot_collapse_to_the_same_option_shape`. A regression
5284        // that promoted the missing-`status` corner to returning a
5285        // typed error (via a signature change to `Result<_, _>`) —
5286        // or that widened the empty-slot corner to a synthetic
5287        // `Some(Utc::now())` at the substrate — would surface here
5288        // rather than as silent operator-facing divergence between
5289        // a never-status-written Process and a phase-since-cleared
5290        // Process at the pool reconciler's per-member row builder.
5291        let mut p_no_status = Process::new("api", empty_spec());
5292        p_no_status.status = None;
5293        let p_empty_slot = process_with_phase_since(None);
5294        assert_eq!(
5295            p_no_status.observed_phase_since().is_none(),
5296            p_empty_slot.observed_phase_since().is_none()
5297        );
5298        assert_eq!(
5299            p_no_status.observed_phase_since().is_some(),
5300            p_empty_slot.observed_phase_since().is_some()
5301        );
5302    }
5303
5304    #[test]
5305    fn observed_phase_since_composes_with_unwrap_or_else_utc_now_tail_at_pool_seed() {
5306        // Call-site-shape pin: the `tatara-pool-reconciler::
5307        // controller_pool::reconcile_inner` per-owned-Process
5308        // `PoolMember { entered_state_at: … }` seed composes
5309        // `p.observed_phase_since().unwrap_or_else(Utc::now)`. A
5310        // regression that returned `Some(Utc::now())` on the empty
5311        // corner (folding the sink into the primitive) would break
5312        // the observable contract that a caller with a distinct
5313        // now-source (e.g. an injected `time_source: impl Fn() ->
5314        // DateTime<Utc>`, or a test-time frozen clock) could
5315        // substitute at the tail — this pin binds the empty-corner
5316        // shape by observing that the substrate returns `None` (so
5317        // the `.unwrap_or_else` runs at the callsite) and that the
5318        // populated-corner shape is byte-identical between the
5319        // substrate `Some(anchor)` and the composed
5320        // `Some(anchor).unwrap_or_else(...)` (the fallback never
5321        // fires when the corner is populated). Peer to
5322        // `created_at_composes_with_signed_duration_since_at_ttl_gate`
5323        // on the metadata-timestamp side — both bind the
5324        // composition shape at the callsite so a substrate-side
5325        // refactor cannot silently break the tail semantics.
5326        let anchor = crate::time::seconds_ago(30);
5327        // Populated corner: substrate returns `Some(anchor)` and
5328        // the composed tail returns `anchor` (fallback silent).
5329        let p = process_with_phase_since(Some(anchor));
5330        let composed = p.observed_phase_since().unwrap_or_else(Utc::now);
5331        assert_eq!(composed, anchor);
5332        // Empty corner: substrate returns `None` and the composed
5333        // tail fires `Utc::now()` at the callsite (observed as a
5334        // timestamp >= a `before` sample AND close to now).
5335        let before = Utc::now();
5336        let p = process_with_phase_since(None);
5337        assert!(p.observed_phase_since().is_none());
5338        let composed = p.observed_phase_since().unwrap_or_else(Utc::now);
5339        assert!(composed >= before);
5340        assert!(composed <= crate::time::seconds_from_now(1));
5341    }
5342
5343    // ─── Process::observed_phase_since_or substrate pins ────────────────
5344    //
5345    // Pins the pure composer over `Process::observed_phase_since` that
5346    // owns the paired `.observed_phase_since().unwrap_or_else(Utc::now)`
5347    // chain the pool-reconciler consumer restated by hand pre-lift
5348    // (`tatara-pool-reconciler::controller_pool::reconcile_inner`) and
5349    // that the sibling `observed_phase_since_composes_with_unwrap_or_
5350    // else_utc_now_tail_at_pool_seed` call-site-shape pin binds at
5351    // fail-before-pass-after granularity above. Peer to the sibling
5352    // `created_at_or_*` pin family — both bind the pure-composer
5353    // discipline (fallback owned by the caller, wall-clock read stays
5354    // at the callsite) at one substrate accessor per axis. Fail-before-
5355    // pass-after granularity: `observed_phase_since_or` did not exist
5356    // pre-lift, so any test invoking it fails to compile pre-lift and
5357    // passes post-lift.
5358
5359    #[test]
5360    fn observed_phase_since_or_returns_fallback_when_status_is_none() {
5361        // Missing-`status` corner pin: the composer collapses the
5362        // no-status case to the caller's fallback anchor byte-
5363        // identically to the pre-lift `.unwrap_or(fallback)` tail on
5364        // the `.and_then(|s| s.phase_since)` pure projection. A
5365        // freshly-forked Process whose reconciler has not yet stamped
5366        // a first phase-transition gets the caller's wall-clock read
5367        // (or a test's frozen anchor) synthesized so downstream
5368        // dwell-time / tie-break arithmetic proceeds without a
5369        // special-case branch at each consumer.
5370        let mut p = Process::new("api", empty_spec());
5371        p.status = None;
5372        let fallback = crate::time::seconds_ago(42);
5373        assert_eq!(p.observed_phase_since_or(fallback), fallback);
5374    }
5375
5376    #[test]
5377    fn observed_phase_since_or_returns_fallback_when_slot_is_empty() {
5378        // Populated-`status` + empty-slot corner pin: a
5379        // `ProcessStatus` whose `phase_since` slot is `None`
5380        // collapses to the caller's fallback at the composer. Peer
5381        // to the missing-`status` corner above — both compose the
5382        // `None` output of the pure projection through the same
5383        // `.unwrap_or(fallback)` tail. A regression that returned
5384        // the fallback ONLY on the missing-`status` corner (and
5385        // panicked / returned a stale sentinel on the empty-slot
5386        // corner) would silently break the pool-reconciler's
5387        // per-member row builder on freshly-forked members whose
5388        // reconciler HAD stamped an empty status but not yet a
5389        // first transition.
5390        let p = process_with_phase_since(None);
5391        let fallback = crate::time::seconds_ago(7);
5392        assert_eq!(p.observed_phase_since_or(fallback), fallback);
5393    }
5394
5395    #[test]
5396    fn observed_phase_since_or_returns_anchor_when_slot_is_populated() {
5397        // Populated-slot corner pin: with a populated
5398        // `phase_since` slot, the composer ignores the caller's
5399        // fallback and returns the observed anchor byte-identically
5400        // to the pre-lift `.unwrap_or(fallback)` pass-through.
5401        // Sibling to `observed_phase_since_returns_anchor_when_slot_
5402        // is_populated` — that pin binds the pure projection, this
5403        // pin binds the composer's pass-through on the same
5404        // populated corner.
5405        let anchor = crate::time::seconds_ago(300);
5406        let p = process_with_phase_since(Some(anchor));
5407        let unrelated_fallback = crate::time::seconds_from_now(9_999);
5408        assert_eq!(p.observed_phase_since_or(unrelated_fallback), anchor);
5409    }
5410
5411    #[test]
5412    fn observed_phase_since_or_is_pure_over_the_fallback_argument() {
5413        // Purity pin: the composer itself never reads the wall clock
5414        // — two consecutive calls with the SAME fallback return
5415        // byte-identical `DateTime<Utc>` values on both the missing-
5416        // slot corner (both calls return the caller's fallback) and
5417        // the populated-slot corner (both calls return the observed
5418        // anchor). Peer to the sibling `created_at_or_is_pure_over_
5419        // the_fallback_argument` pin; both bind the pure-composer
5420        // discipline on the ONE substrate accessor per timestamp
5421        // axis.
5422        let fallback = crate::time::seconds_ago(7);
5423        // Missing status.
5424        let mut p = Process::new("x", empty_spec());
5425        p.status = None;
5426        assert_eq!(
5427            p.observed_phase_since_or(fallback),
5428            p.observed_phase_since_or(fallback),
5429        );
5430        // Populated slot.
5431        let anchor = crate::time::seconds_ago(120);
5432        let p = process_with_phase_since(Some(anchor));
5433        assert_eq!(
5434            p.observed_phase_since_or(fallback),
5435            p.observed_phase_since_or(fallback),
5436        );
5437    }
5438
5439    #[test]
5440    fn observed_phase_since_or_matches_pre_lift_unwrap_or_chain_shape() {
5441        // Parity pin: sweeps the three corners every pre-lift
5442        // consumer encountered (missing status, empty slot, populated
5443        // slot) and compares the substrate call against the hand-
5444        // authored pre-lift `.observed_phase_since().unwrap_or(
5445        // fallback)` chain byte-identically. A regression that
5446        // reshaped either corner (returning the fallback on a
5447        // populated slot, returning a sentinel like `DateTime::MIN`
5448        // on the missing corner regardless of the caller's fallback)
5449        // would surface here rather than as silent operator-facing
5450        // skew between the pool convergence snapshot's observed-
5451        // transition anchor and any future observed-transition
5452        // consumer on the SAME `Process` within one reconcile pass.
5453        fn pre_lift(p: &Process, fallback: DateTime<Utc>) -> DateTime<Utc> {
5454            p.observed_phase_since().unwrap_or(fallback)
5455        }
5456        let fallback = crate::time::seconds_ago(13);
5457        // Missing status.
5458        let mut p = Process::new("x", empty_spec());
5459        p.status = None;
5460        assert_eq!(p.observed_phase_since_or(fallback), pre_lift(&p, fallback),);
5461        // Empty slot.
5462        let p = process_with_phase_since(None);
5463        assert_eq!(p.observed_phase_since_or(fallback), pre_lift(&p, fallback),);
5464        // Populated slot.
5465        let anchor = crate::time::seconds_ago(42);
5466        let p = process_with_phase_since(Some(anchor));
5467        assert_eq!(p.observed_phase_since_or(fallback), pre_lift(&p, fallback),);
5468    }
5469
5470    #[test]
5471    fn observed_phase_since_or_composes_with_utc_now_at_reconciler_callsite() {
5472        // Call-site-shape pin: the production consumer
5473        // (`controller_pool::reconcile_inner`'s per-owned-Process
5474        // `PoolMember { entered_state_at, .. }` seed) calls
5475        // `p.observed_phase_since_or(Utc::now())`. On the populated
5476        // corner the wall-clock fallback is irrelevant (the observed
5477        // anchor wins); on the missing/empty corner the fallback
5478        // becomes the resolved value within the sub-second window
5479        // between the caller's `Utc::now()` read and the assertion
5480        // below. This pin binds that the callsite composition
5481        // returns the observed anchor exactly on the populated
5482        // corner (the stable, drift-free assertion) and a "recent"
5483        // wall-clock read on the empty corner (bounded within a
5484        // two-second window to absorb scheduler jitter). A
5485        // regression that silently substituted a different fallback
5486        // (`DateTime::MIN`, a per-cluster prefix offset, a
5487        // hardcoded epoch) would surface at the second half of this
5488        // pin.
5489        // Populated corner: byte-identical to the observed anchor.
5490        let anchor = crate::time::seconds_ago(600);
5491        let p = process_with_phase_since(Some(anchor));
5492        assert_eq!(p.observed_phase_since_or(Utc::now()), anchor);
5493        // Missing status corner: within a two-second wall-clock window.
5494        let mut p = Process::new("x", empty_spec());
5495        p.status = None;
5496        let before = Utc::now();
5497        let resolved = p.observed_phase_since_or(Utc::now());
5498        let after = Utc::now();
5499        assert!(
5500            resolved >= before - chrono::Duration::seconds(2),
5501            "resolved {resolved} is before window start {before}"
5502        );
5503        assert!(
5504            resolved <= after + chrono::Duration::seconds(2),
5505            "resolved {resolved} is after window end {after}"
5506        );
5507    }
5508
5509    // ─── Process::is_being_deleted substrate pins ───────────────────────
5510    //
5511    // Pins the copy-form metadata-projection primitive on the
5512    // deletion-tombstone axis. Peer to the borrow-form + copy-form
5513    // metadata-fallback family (`namespace_or_default`,
5514    // `name_or_placeholder`, `uid_or_empty`, `coordinates_or_defaults`,
5515    // `coordinates_or_none`, `owned_coordinates_or_err`, `annotation`);
5516    // this one opens the presence-probe corner for the tombstone slot.
5517    // Fail-before-pass-after granularity: `is_being_deleted` did not
5518    // exist pre-lift, so any test invoking it fails to compile pre-
5519    // lift and passes post-lift.
5520
5521    fn tombstoned_process() -> Process {
5522        let mut p = Process::new("api-gateway", empty_spec());
5523        p.metadata.namespace = Some("prod".into());
5524        // Routes through the ONE substrate composer
5525        // `tatara_process::time::tombstone_now` — one of 12 pre-lift
5526        // exact-match sites past the ★★ PRIME-DIRECTIVE ≥ 2 threshold
5527        // for the `Some(Time(Utc::now()))` wire shape.
5528        p.metadata.deletion_timestamp = crate::time::tombstone_now();
5529        p
5530    }
5531
5532    #[test]
5533    fn is_being_deleted_returns_false_when_deletion_timestamp_is_absent() {
5534        // Missing-tombstone corner pin: the primitive collapses the
5535        // no-tombstone case to `false` so the SIGTERM preempt at
5536        // `controller::reconcile` skips the `→ Exiting` forcing
5537        // branch and the DELETE-skip at `handle_exiting`'s child
5538        // fan-out does NOT `continue` past a child that is still
5539        // healthy. Matches the pre-lift `.is_some()` chain's `false`
5540        // byte-identically at every consumer's downstream gate.
5541        let mut p = Process::new("api", empty_spec());
5542        p.metadata.deletion_timestamp = None;
5543        assert!(!p.is_being_deleted());
5544    }
5545
5546    #[test]
5547    fn is_being_deleted_returns_true_when_deletion_timestamp_is_present() {
5548        // Present-tombstone corner pin: the primitive returns
5549        // `true` on any populated `metadata.deletionTimestamp`
5550        // slot regardless of the timestamp payload — the two
5551        // consumers only read the tombstone's PRESENCE, never
5552        // its RFC-3339 timestamp value. A regression that gated
5553        // the `true` return on the timestamp being non-epoch, or
5554        // parsed the timestamp before returning, would surface
5555        // here rather than as silent skew at the SIGTERM preempt
5556        // or child-fan-out DELETE-skip on the SAME `Process`.
5557        let p = tombstoned_process();
5558        assert!(p.is_being_deleted());
5559    }
5560
5561    #[test]
5562    fn is_being_deleted_is_a_pure_projection() {
5563        // Purity pin: two consecutive calls return byte-identical
5564        // `bool` values (no lazy materialization, no interior
5565        // mutation of `self`). Peer to the sibling
5566        // `observed_phase_is_a_pure_projection` +
5567        // `observed_pid_is_a_pure_projection` +
5568        // `observed_flux_resources_is_a_pure_projection` +
5569        // `observed_attestation_is_a_pure_projection` pins; all
5570        // five bind the pure-projection discipline on the ONE
5571        // substrate accessor per metadata / status slot.
5572        let p = tombstoned_process();
5573        let a = p.is_being_deleted();
5574        let b = p.is_being_deleted();
5575        assert_eq!(a, b);
5576        assert!(a);
5577    }
5578
5579    #[test]
5580    fn is_being_deleted_matches_pre_lift_reconciler_chain_shape() {
5581        // Parity pin: sweeps the two corners every pre-lift
5582        // consumer plausibly encountered (missing tombstone,
5583        // present tombstone) and compares the substrate call
5584        // against a hand-authored pre-lift chain byte-identically.
5585        // A regression that reshaped either corner would surface
5586        // here rather than as silent operator-facing skew between
5587        // the top-level dispatcher's SIGTERM preempt and the
5588        // SIGTERM cascade's child-fan-out DELETE-skip on the
5589        // SAME `Process` within one reconcile pass.
5590        fn pre_lift(p: &Process) -> bool {
5591            p.metadata.deletion_timestamp.is_some()
5592        }
5593        let mut p = Process::new("api", empty_spec());
5594        p.metadata.deletion_timestamp = None;
5595        assert_eq!(p.is_being_deleted(), pre_lift(&p));
5596        let p = tombstoned_process();
5597        assert_eq!(p.is_being_deleted(), pre_lift(&p));
5598    }
5599
5600    #[test]
5601    fn is_being_deleted_composes_with_process_phase_is_alive_at_reconcile_preempt() {
5602        // Call-site-shape pin: the `controller::reconcile` SIGTERM
5603        // preempt composes `is_being_deleted() && current_phase
5604        // .is_alive()` — the tombstone-presence probe AND the
5605        // alive-phase gate must BOTH hold to force `→ Exiting`.
5606        // A dead-phase (`Zombie` / `Reaped` / `Failed`) Process
5607        // that carries a tombstone still runs its normal handler,
5608        // not the preempt. This pin binds that composition shape
5609        // at the primitive so a regression that flipped either
5610        // half of the `&&` (or that broadened the tombstone probe
5611        // to include the `is_alive` half implicitly) surfaces
5612        // here rather than as silent skew at the top-level
5613        // dispatch on the SAME `Process`.
5614        let mut p = tombstoned_process();
5615        // Alive + tombstoned → preempt fires.
5616        let mut alive = ProcessStatus::default();
5617        alive.phase = ProcessPhase::Running;
5618        p.status = Some(alive);
5619        assert!(p.is_being_deleted());
5620        assert!(p.observed_phase().unwrap_or_default().is_alive());
5621        // Dead + tombstoned → preempt does NOT fire (composition
5622        // with `is_alive` returns false).
5623        let mut dead = ProcessStatus::default();
5624        dead.phase = ProcessPhase::Reaped;
5625        p.status = Some(dead);
5626        assert!(p.is_being_deleted());
5627        assert!(!p.observed_phase().unwrap_or_default().is_alive());
5628    }
5629
5630    // ─── Process::created_at substrate pins ─────────────────────────
5631    //
5632    // Pins the copy-form metadata-projection primitive on the
5633    // `metadata.creationTimestamp` axis that owns the
5634    // `.metadata.creation_timestamp.as_ref().map(|t| t.0)` chain the
5635    // three hand-authored sites (`lifetime_clock::evaluate`,
5636    // `lifetime_clock::requeue_with_ttl`,
5637    // `tatara-reconciler::table_controller`) restated by hand pre-lift.
5638    // Peer to the sibling `is_being_deleted_*` +
5639    // `observed_phase_*` pin families — all three primitives project a
5640    // wire-format `Option<T>` slot into a `Copy` inner value at ONE
5641    // owner. Fail-before-pass-after granularity: `created_at` did not
5642    // exist pre-lift, so any test invoking it fails to compile pre-lift
5643    // and passes post-lift.
5644
5645    fn creation_stamped_process(t: DateTime<Utc>) -> Process {
5646        let mut p = Process::new("age-anchor", empty_spec());
5647        p.metadata.namespace = Some("prod".into());
5648        // Routes through the ONE substrate primitive
5649        // `crate::time::creation_stamp_at` — the anchor-explicit peer of
5650        // `crate::time::tombstone_at` on the (creation, deletion) axis
5651        // of the `ObjectMeta` metadata-Time slots. Pre-lift this site
5652        // restated the fully-qualified 5-token
5653        // `Some(k8s_openapi::apimachinery::pkg::apis::meta::v1::Time(t))`
5654        // wire wrap by hand; post-lift the K8s Time wrap + Option wrap
5655        // sinks live at ONE substrate owner alongside the deletion-slot
5656        // peer.
5657        p.metadata.creation_timestamp = crate::time::creation_stamp_at(t);
5658        p
5659    }
5660
5661    #[test]
5662    fn created_at_returns_none_when_creation_timestamp_is_absent() {
5663        // Missing-slot corner pin: the primitive collapses the
5664        // no-creation-timestamp case to `None` so the TTL-expiry gate
5665        // at `lifetime_clock::evaluate` short-circuits its inner
5666        // `if let Some(...)` branch (no elapsed computation), the
5667        // requeue-budget picker returns its default sleep, and the
5668        // stable-name arbiter's `.unwrap_or_else(Utc::now)` tail
5669        // synthesizes a "just created" anchor at its own site. Matches
5670        // the pre-lift `.as_ref().map(|t| t.0)` chain's `None`
5671        // byte-identically at every consumer's downstream tail.
5672        let mut p = Process::new("api", empty_spec());
5673        p.metadata.creation_timestamp = None;
5674        assert!(p.created_at().is_none());
5675    }
5676
5677    #[test]
5678    fn created_at_returns_some_datetime_when_slot_is_populated() {
5679        // Populated-slot corner pin: with a populated
5680        // `metadata.creationTimestamp` slot, the primitive unwraps the
5681        // wire-format `Time` newtype to its inner `DateTime<Utc>` and
5682        // returns it as `Some(datetime)` — hiding the `.0` field-access
5683        // every pre-lift consumer restated to reach the underlying
5684        // instant.
5685        let anchor = crate::time::seconds_ago(300);
5686        let p = creation_stamped_process(anchor);
5687        assert_eq!(p.created_at(), Some(anchor));
5688    }
5689
5690    #[test]
5691    fn created_at_is_a_pure_projection() {
5692        // Purity pin: two consecutive calls return byte-identical
5693        // `Option<DateTime<Utc>>` values (no lazy materialization, no
5694        // interior mutation of `self`). Peer to the sibling
5695        // `is_being_deleted_is_a_pure_projection` +
5696        // `observed_phase_is_a_pure_projection` pins; all three bind
5697        // the pure-projection discipline on the ONE substrate accessor
5698        // per metadata / status slot.
5699        let anchor = Utc::now();
5700        let p = creation_stamped_process(anchor);
5701        let a = p.created_at();
5702        let b = p.created_at();
5703        assert_eq!(a, b);
5704        assert_eq!(a, Some(anchor));
5705    }
5706
5707    #[test]
5708    fn created_at_matches_pre_lift_creation_timestamp_chain_shape() {
5709        // Parity pin: sweeps the two corners every pre-lift consumer
5710        // plausibly encountered (missing slot, populated slot) and
5711        // compares the substrate call against a hand-authored pre-lift
5712        // chain byte-identically. A regression that reshaped either
5713        // corner (returning `Some(Utc::now())` on the missing slot,
5714        // returning a rounded / truncated timestamp on the populated
5715        // slot) would surface here rather than as silent operator-
5716        // facing skew between the TTL-expiry gate, the requeue-budget
5717        // picker, and the stable-name claim-arbiter tie-break on the
5718        // SAME `Process` within one reconcile pass.
5719        fn pre_lift(p: &Process) -> Option<DateTime<Utc>> {
5720            p.metadata.creation_timestamp.as_ref().map(|t| t.0)
5721        }
5722        // Missing slot.
5723        let mut p = Process::new("x", empty_spec());
5724        p.metadata.creation_timestamp = None;
5725        assert_eq!(p.created_at(), pre_lift(&p));
5726        // Populated slot.
5727        let anchor = crate::time::seconds_ago(42);
5728        let p = creation_stamped_process(anchor);
5729        assert_eq!(p.created_at(), pre_lift(&p));
5730    }
5731
5732    #[test]
5733    fn created_at_composes_with_signed_duration_since_at_ttl_gate() {
5734        // Call-site-shape pin: the `lifetime_clock::evaluate` TTL-
5735        // expiry gate composes `now.signed_duration_since(creation)`
5736        // where `creation` is the `DateTime<Utc>` returned by this
5737        // primitive's `Some` corner. A regression that returned a
5738        // per-callsite `Local` timezone (or that stripped the timezone
5739        // marker) would break the arithmetic silently. This pin
5740        // computes the elapsed duration byte-identically against the
5741        // pre-lift `.map(|t| t.0)` chain so a timezone drift surfaces
5742        // here rather than as silent skew at the TTL-expiry decision
5743        // on the SAME `Process` within one reconcile pass.
5744        let now = Utc::now();
5745        let anchor = now - chrono::Duration::seconds(120);
5746        let p = creation_stamped_process(anchor);
5747        let via_primitive = p.created_at().expect("populated slot");
5748        let via_pre_lift = p
5749            .metadata
5750            .creation_timestamp
5751            .as_ref()
5752            .map(|t| t.0)
5753            .expect("populated slot");
5754        assert_eq!(
5755            now.signed_duration_since(via_primitive),
5756            now.signed_duration_since(via_pre_lift)
5757        );
5758    }
5759
5760    // ─── Process::created_at_or substrate pins ──────────────────────
5761    //
5762    // Pins the pure composer over `Process::created_at` that owns the
5763    // paired `.created_at().unwrap_or_else(Utc::now)` chain the two
5764    // production consumers restated by hand pre-lift
5765    // (`tatara-reconciler::table_controller::reconcile_process_table`
5766    // + `tatara-pool-reconciler::controller_pool::reconcile_inner`).
5767    // Fail-before-pass-after granularity: `created_at_or` did not
5768    // exist pre-lift, so any test invoking it fails to compile
5769    // pre-lift and passes post-lift.
5770
5771    #[test]
5772    fn created_at_or_returns_fallback_when_creation_timestamp_is_absent() {
5773        // Missing-slot corner pin: the composer collapses the
5774        // no-creation-timestamp case to the caller's fallback anchor
5775        // byte-identically to the pre-lift `.unwrap_or(fallback)`
5776        // tail. A freshly-forked Process whose API server has not yet
5777        // stamped `metadata.creationTimestamp` gets the caller's
5778        // wall-clock read (or a test's frozen anchor) synthesized so
5779        // downstream dwell-time / tie-break arithmetic proceeds
5780        // without a special-case branch at each consumer.
5781        let mut p = Process::new("api", empty_spec());
5782        p.metadata.creation_timestamp = None;
5783        let fallback = crate::time::seconds_ago(42);
5784        assert_eq!(p.created_at_or(fallback), fallback);
5785    }
5786
5787    #[test]
5788    fn created_at_or_returns_anchor_when_slot_is_populated() {
5789        // Populated-slot corner pin: with a populated
5790        // `metadata.creationTimestamp` slot, the composer ignores the
5791        // caller's fallback and returns the observed anchor
5792        // byte-identically to the pre-lift `.unwrap_or(fallback)`
5793        // pass-through. Sibling to `created_at_returns_some_datetime_
5794        // when_slot_is_populated` — that pin binds the pure projection,
5795        // this pin binds the composer's pass-through on the same
5796        // populated corner.
5797        let anchor = crate::time::seconds_ago(300);
5798        let p = creation_stamped_process(anchor);
5799        let unrelated_fallback = crate::time::seconds_from_now(9_999);
5800        assert_eq!(p.created_at_or(unrelated_fallback), anchor);
5801    }
5802
5803    #[test]
5804    fn created_at_or_is_pure_over_the_fallback_argument() {
5805        // Purity pin: the composer itself never reads the wall clock —
5806        // two consecutive calls with the SAME fallback return
5807        // byte-identical `DateTime<Utc>` values on both the missing-
5808        // slot corner (both calls return the caller's fallback) and
5809        // the populated-slot corner (both calls return the observed
5810        // anchor). Peer to the sibling
5811        // `created_at_is_a_pure_projection` pin; both bind the pure-
5812        // projection / pure-composer discipline on the ONE substrate
5813        // accessor per axis.
5814        let fallback = crate::time::seconds_ago(7);
5815        // Missing slot.
5816        let mut p = Process::new("x", empty_spec());
5817        p.metadata.creation_timestamp = None;
5818        assert_eq!(p.created_at_or(fallback), p.created_at_or(fallback));
5819        // Populated slot.
5820        let anchor = crate::time::seconds_ago(120);
5821        let p = creation_stamped_process(anchor);
5822        assert_eq!(p.created_at_or(fallback), p.created_at_or(fallback));
5823    }
5824
5825    #[test]
5826    fn created_at_or_matches_pre_lift_unwrap_or_chain_shape() {
5827        // Parity pin: sweeps the two corners every pre-lift consumer
5828        // encountered (missing slot, populated slot) and compares the
5829        // substrate call against the hand-authored pre-lift
5830        // `.created_at().unwrap_or(fallback)` chain byte-identically.
5831        // A regression that reshaped either corner (returning the
5832        // fallback on a populated slot, returning `Utc::now()` on the
5833        // missing slot regardless of the caller's fallback) would
5834        // surface here rather than as silent operator-facing skew
5835        // between the claim-arbiter's tie-break anchor and the pool
5836        // convergence snapshot's dwell-time anchor on the SAME
5837        // `Process` within one reconcile pass.
5838        fn pre_lift(p: &Process, fallback: DateTime<Utc>) -> DateTime<Utc> {
5839            p.created_at().unwrap_or(fallback)
5840        }
5841        let fallback = crate::time::seconds_ago(13);
5842        // Missing slot.
5843        let mut p = Process::new("x", empty_spec());
5844        p.metadata.creation_timestamp = None;
5845        assert_eq!(p.created_at_or(fallback), pre_lift(&p, fallback));
5846        // Populated slot.
5847        let anchor = crate::time::seconds_ago(42);
5848        let p = creation_stamped_process(anchor);
5849        assert_eq!(p.created_at_or(fallback), pre_lift(&p, fallback));
5850    }
5851
5852    #[test]
5853    fn created_at_or_composes_with_utc_now_at_reconciler_callsites() {
5854        // Call-site-shape pin: the two production consumers
5855        // (`table_controller::reconcile_process_table` +
5856        // `controller_pool::reconcile_inner`) both call
5857        // `p.created_at_or(Utc::now())`. On the populated corner the
5858        // wall-clock fallback is irrelevant (the observed anchor
5859        // wins); on the missing corner the fallback becomes the
5860        // resolved value within the sub-second window between the
5861        // caller's `Utc::now()` read and the assertion below. This
5862        // pin binds that the callsite composition returns the
5863        // observed anchor exactly on the populated corner (the
5864        // stable, drift-free assertion) and a "recent" wall-clock
5865        // read on the missing corner (bounded within a two-second
5866        // window to absorb scheduler jitter). A regression that
5867        // silently substituted a different fallback (`DateTime::MIN`,
5868        // a per-cluster prefix offset, a hardcoded epoch) would
5869        // surface at the second half of this pin.
5870        // Populated corner: byte-identical to the observed anchor.
5871        let anchor = crate::time::seconds_ago(600);
5872        let p = creation_stamped_process(anchor);
5873        assert_eq!(p.created_at_or(Utc::now()), anchor);
5874        // Missing corner: within a two-second wall-clock window.
5875        let mut p = Process::new("x", empty_spec());
5876        p.metadata.creation_timestamp = None;
5877        let before = Utc::now();
5878        let resolved = p.created_at_or(Utc::now());
5879        let after = Utc::now();
5880        assert!(
5881            resolved >= before - chrono::Duration::seconds(2),
5882            "resolved {resolved} is before window start {before}"
5883        );
5884        assert!(
5885            resolved <= after + chrono::Duration::seconds(2),
5886            "resolved {resolved} is after window end {after}"
5887        );
5888    }
5889
5890    // ─── Process::created_at_or_now substrate pins ──────────────────
5891    //
5892    // Pins the wall-clock-anchored peer of `Process::created_at_or` —
5893    // the ONE substrate owner of the 2-arg `p.created_at_or(Utc::now())`
5894    // chain the two production consumers hand-authored pre-lift
5895    // (`tatara-reconciler::table_controller::reconcile_process_table`
5896    // + `tatara-pool-reconciler::controller_pool::reconcile_inner`).
5897    // Fail-before-pass-after granularity: `created_at_or_now` did not
5898    // exist pre-lift, so any test invoking it fails to compile pre-lift
5899    // and passes post-lift.
5900
5901    #[test]
5902    fn created_at_or_now_returns_wall_clock_when_creation_timestamp_is_absent() {
5903        // Missing-slot corner pin: the peer stamps the wall-clock read
5904        // as the resolved anchor byte-identically to
5905        // `p.created_at_or(Utc::now())` — bounded within a two-second
5906        // window to absorb scheduler jitter between the pin's own
5907        // `Utc::now()` reads and the peer's internal read. A regression
5908        // that silently substituted a different fallback source
5909        // (`DateTime::MIN`, a cached-at-module-load constant, a
5910        // per-namespace override) would surface at this window rather
5911        // than as silent tie-break skew at the claim-arbiter row seed
5912        // or dwell-time skew at the pool convergence snapshot.
5913        let mut p = Process::new("x", empty_spec());
5914        p.metadata.creation_timestamp = None;
5915        let before = Utc::now();
5916        let resolved = p.created_at_or_now();
5917        let after = Utc::now();
5918        assert!(
5919            resolved >= before - chrono::Duration::seconds(2),
5920            "resolved {resolved} is before window start {before}"
5921        );
5922        assert!(
5923            resolved <= after + chrono::Duration::seconds(2),
5924            "resolved {resolved} is after window end {after}"
5925        );
5926    }
5927
5928    #[test]
5929    fn created_at_or_now_returns_anchor_when_slot_is_populated() {
5930        // Populated-slot corner pin: with a populated
5931        // `metadata.creationTimestamp` slot, the peer's internal
5932        // `Utc::now()` fallback is irrelevant and the observed anchor
5933        // wins byte-identically to the 2-arg
5934        // `p.created_at_or(<any-fallback>)` pass-through. Sibling to
5935        // the peer `created_at_or_returns_anchor_when_slot_is_populated`
5936        // pin — both bind the pass-through discipline on the same
5937        // populated corner, one on the pure composer and one on the
5938        // wall-clock-anchored peer.
5939        let anchor = crate::time::seconds_ago(300);
5940        let p = creation_stamped_process(anchor);
5941        assert_eq!(p.created_at_or_now(), anchor);
5942    }
5943
5944    #[test]
5945    fn created_at_or_now_reads_wall_clock_at_call_time_not_module_load() {
5946        // Per-invocation wall-clock-read pin: two consecutive calls on
5947        // a missing-slot Process must return DISTINCT (or at least
5948        // monotonically-non-decreasing) `DateTime<Utc>` values, since
5949        // each call reads a fresh `Utc::now()`. A regression that
5950        // hoisted the wall-clock read to a stale module-load constant
5951        // (or cached the first-invocation value inside `Self`) would
5952        // return the SAME value on the second call — this pin surfaces
5953        // that regression directly, matching the peer-family discipline
5954        // on `PoolStatus::observed_now` / `AllocationStatus::transition_now`
5955        // / `lifetime_clock::evaluate_now` where each invocation reads
5956        // its own `Utc::now()` at the primitive's body.
5957        let mut p = Process::new("x", empty_spec());
5958        p.metadata.creation_timestamp = None;
5959        let first = p.created_at_or_now();
5960        // A `std::thread::sleep(...)` here would be flaky under CI clock
5961        // jitter; the monotonicity check (each call is >= previous)
5962        // suffices to catch the module-load-constant regression class
5963        // because two module-load-constant reads would return identical
5964        // values on a `chrono::DateTime<Utc>` field (equality, not
5965        // ordering, is what the regression breaks).
5966        let second = p.created_at_or_now();
5967        assert!(
5968            second >= first,
5969            "second `created_at_or_now` read {second} must be >= first {first}; \
5970             a regression that cached the wall-clock read at module load \
5971             would return byte-identical values"
5972        );
5973    }
5974
5975    #[test]
5976    fn created_at_or_now_matches_created_at_or_with_utc_now_bytewise() {
5977        // Delegation pin: the peer's body is `self.created_at_or(Utc::now())`
5978        // — a pure delegation, not a re-implementation. On the
5979        // populated corner both surfaces return the observed anchor
5980        // byte-identically (wall-clock fallback is irrelevant). A
5981        // regression that re-implemented the peer with different
5982        // semantics (a different fallback source, a per-slot override
5983        // that only applied to one surface) would surface at the
5984        // populated-corner half of this pin.
5985        let anchor = crate::time::seconds_ago(600);
5986        let p = creation_stamped_process(anchor);
5987        assert_eq!(p.created_at_or_now(), p.created_at_or(Utc::now()));
5988        assert_eq!(p.created_at_or_now(), anchor);
5989    }
5990
5991    #[test]
5992    fn created_at_or_now_composes_at_reconciler_callsites_verbatim() {
5993        // Cross-callsite parity pin: both production consumers
5994        // (`table_controller::reconcile_process_table` +
5995        // `controller_pool::reconcile_inner`) pre-lift called
5996        // `p.created_at_or(Utc::now())` inline; post-lift both call
5997        // `p.created_at_or_now()`. This pin sweeps both the populated
5998        // and missing corners on the SAME `Process` fixture and asserts
5999        // that both surfaces (pre-lift chain, post-lift peer) resolve
6000        // to the same anchor on the populated corner. The missing
6001        // corner is elided from this specific pin because the pre-lift
6002        // and post-lift `Utc::now()` reads happen at different call
6003        // sites (across the `p.created_at_or(Utc::now())` argument
6004        // evaluation vs. the peer's body), so an exact-equality
6005        // assertion between the two reads would race the wall clock —
6006        // the `_reads_wall_clock_at_call_time_not_module_load` pin
6007        // above already binds the per-invocation freshness invariant
6008        // on the missing corner without needing the cross-shape
6009        // equality here.
6010        let anchor = crate::time::seconds_ago(120);
6011        let p = creation_stamped_process(anchor);
6012        let pre_lift_shape = p.created_at_or(Utc::now());
6013        let post_lift_shape = p.created_at_or_now();
6014        assert_eq!(pre_lift_shape, anchor);
6015        assert_eq!(post_lift_shape, anchor);
6016        assert_eq!(pre_lift_shape, post_lift_shape);
6017    }
6018
6019    // ─── Process::resolved_ephemeral substrate pins ─────────────────
6020    //
6021    // Pins the compound spec-projection primitive on the
6022    // `spec.lifetime` axis that owns the ambiguity-aware
6023    // `resolved_ephemeral` chain the three hand-authored sites
6024    // (`lifetime_clock::evaluate`, `lifetime_clock::requeue_with_ttl`,
6025    // `tatara-reconciler::render::render_export_jobs`) restated by
6026    // hand pre-lift through TWO different chains that disagreed on
6027    // the ambiguous corner. Fail-before-pass-after granularity:
6028    // `resolved_ephemeral` did not exist pre-lift on `impl Process`,
6029    // so any test invoking it fails to compile pre-lift and passes
6030    // post-lift.
6031
6032    fn permanent_only_process() -> Process {
6033        let mut spec = empty_spec();
6034        // Routes through the ONE substrate composer
6035        // [`crate::lifetime::Lifetime::permanent`] — one of FOUR
6036        // pre-lift exact-match sites past the ★★ PRIME-DIRECTIVE ≥ 2
6037        // threshold; see the composer's doc-comment for the full
6038        // migration rationale.
6039        spec.lifetime = crate::lifetime::Lifetime::permanent();
6040        Process::new("perm", spec)
6041    }
6042
6043    fn ephemeral_only_process(ttl: &str) -> Process {
6044        let mut spec = empty_spec();
6045        // Routes through the ONE substrate composer
6046        // [`crate::lifetime::Lifetime::ephemeral`] — one of ELEVEN+
6047        // pre-lift exact-match sites past the ★★ PRIME-DIRECTIVE ≥ 2
6048        // threshold; see the composer's doc-comment for the full
6049        // migration rationale.
6050        spec.lifetime = crate::lifetime::Lifetime::ephemeral(EphemeralLifetime {
6051            ttl: ttl.into(),
6052            teardown_policy: crate::lifetime::TeardownPolicy::OnAttested,
6053            max_concurrent: 3,
6054            exports: vec![],
6055        });
6056        Process::new("eph", spec)
6057    }
6058
6059    fn ambiguous_lifetime_process() -> Process {
6060        let mut spec = empty_spec();
6061        spec.lifetime = crate::lifetime::Lifetime {
6062            permanent: Some(crate::lifetime::PermanentLifetime {}),
6063            ephemeral: Some(EphemeralLifetime::default()),
6064        };
6065        Process::new("both", spec)
6066    }
6067
6068    #[test]
6069    fn resolved_ephemeral_returns_none_when_lifetime_is_default_empty() {
6070        // Empty-default corner pin: neither slot populated. The
6071        // resolver collapses to `Permanent(&DEFAULT_PERMANENT)` and
6072        // the compound projection sees no ephemeral inner. Matches
6073        // the pre-lift `lifetime_clock::evaluate` early-return to
6074        // `AutoTerminate::Skip` byte-identically.
6075        let p = Process::new("empty-lifetime", empty_spec());
6076        assert!(p.resolved_ephemeral().is_none());
6077    }
6078
6079    #[test]
6080    fn resolved_ephemeral_returns_none_for_permanent_only_process() {
6081        // Permanent-only corner pin: the `permanent:` slot is
6082        // populated, `ephemeral:` is not. Matches the pre-lift
6083        // `lifetime_clock::evaluate` outcome — the teardown/TTL
6084        // branch is never reached on a Permanent Process, and the
6085        // export-render arm now agrees at this call site (was
6086        // previously reached through the raw `.ephemeral.as_ref()`
6087        // that also returned `None` on this same corner — no drift
6088        // here; the drift is at the ambiguous corner below).
6089        let p = permanent_only_process();
6090        assert!(p.resolved_ephemeral().is_none());
6091    }
6092
6093    #[test]
6094    fn resolved_ephemeral_returns_some_for_ephemeral_only_process() {
6095        // Ephemeral-only corner pin: the ONE arm that projects. The
6096        // returned borrow carries the operator-authored `ttl` /
6097        // `teardown_policy` / `max_concurrent` verbatim. A
6098        // regression that swapped the projection to the sibling
6099        // `permanent:` slot would surface here as a type mismatch on
6100        // the `EphemeralLifetime` fields rather than as silent
6101        // operator-facing no-op teardown at the reconciler.
6102        let p = ephemeral_only_process("42m");
6103        let e = p
6104            .resolved_ephemeral()
6105            .expect("ephemeral-only Process must project");
6106        assert_eq!(e.ttl, "42m");
6107        assert_eq!(
6108            e.teardown_policy,
6109            crate::lifetime::TeardownPolicy::OnAttested
6110        );
6111        assert_eq!(e.max_concurrent, 3);
6112    }
6113
6114    #[test]
6115    fn resolved_ephemeral_returns_none_for_ambiguous_lifetime() {
6116        // DRIFT-CLOSING CONTRACT: BOTH `permanent:` AND `ephemeral:`
6117        // slots populated is an operator-authored mis-configuration.
6118        // Pre-lift, `lifetime_clock::evaluate` (via
6119        // `resolved_ephemeral()` on `Lifetime`) collapsed this
6120        // corner to `None` and yielded `AutoTerminate::Skip`, while
6121        // `tatara-reconciler::render::render_export_jobs` walked
6122        // the naked `.spec.lifetime.ephemeral.as_ref()` chain and
6123        // returned `Some(&e)` — so the reconciler would emit export
6124        // Jobs on a Process whose teardown-triggered fire semantics
6125        // the lifetime clock refused to honor. Post-lift this
6126        // primitive collapses ambiguity to `None` at ONE site so
6127        // BOTH consumers agree. A regression that broadened the
6128        // projection back to the raw field (or that silently
6129        // "preferred ephemeral" in the ambiguous case) surfaces
6130        // here rather than as export-Job noise on a mis-configured
6131        // ephemeral.
6132        let p = ambiguous_lifetime_process();
6133        assert!(p.resolved_ephemeral().is_none());
6134        // The raw field IS populated at this corner — pins the
6135        // pre-lift `.spec.lifetime.ephemeral.as_ref()` shape that
6136        // returned `Some` here.
6137        assert!(p.spec.lifetime.ephemeral.is_some());
6138    }
6139
6140    #[test]
6141    fn resolved_ephemeral_matches_spec_lifetime_forwarder() {
6142        // Byte-identity pin: the `Process` projection delegates
6143        // through the underlying `Lifetime::resolved_ephemeral`
6144        // primitive at every corner (empty, permanent-only,
6145        // ephemeral-only, ambiguous). A regression that silently
6146        // reintroduced the raw `.ephemeral.as_ref()` shortcut, or
6147        // that decided the ambiguous case by "prefer ephemeral"
6148        // at the Process layer instead of delegating, surfaces
6149        // here.
6150        for p in [
6151            Process::new("empty", empty_spec()),
6152            permanent_only_process(),
6153            ephemeral_only_process("1h"),
6154            ambiguous_lifetime_process(),
6155        ] {
6156            let via_process = p.resolved_ephemeral();
6157            let via_lifetime = p.spec.lifetime.resolved_ephemeral();
6158            // Both borrows point into the SAME `EphemeralLifetime`
6159            // slot when present — a regression that materialized a
6160            // per-call clone at the Process layer would fail the
6161            // pointer-equality gate.
6162            match (via_process, via_lifetime) {
6163                (Some(a), Some(b)) => assert!(
6164                    std::ptr::eq(a, b),
6165                    "Process::resolved_ephemeral must borrow the same slot as Lifetime::resolved_ephemeral"
6166                ),
6167                (None, None) => {}
6168                (a, b) => panic!(
6169                    "resolved_ephemeral shape drift: process={:?}, lifetime={:?}",
6170                    a.is_some(),
6171                    b.is_some()
6172                ),
6173            }
6174        }
6175    }
6176
6177    #[test]
6178    fn resolved_ephemeral_is_a_pure_projection() {
6179        // Purity pin: two consecutive calls return borrows into the
6180        // same underlying slot (no lazy materialization, no interior
6181        // mutation of `self`). Peer to the sibling
6182        // `is_being_deleted_is_a_pure_projection` +
6183        // `observed_attestation_is_a_pure_projection` pins; all
6184        // three bind the pure-projection discipline on the ONE
6185        // substrate accessor per spec / metadata / status slot.
6186        let p = ephemeral_only_process("5m");
6187        let a = p.resolved_ephemeral();
6188        let b = p.resolved_ephemeral();
6189        match (a, b) {
6190            (Some(x), Some(y)) => assert!(std::ptr::eq(x, y)),
6191            other => panic!("expected two Some borrows into the same slot, got {other:?}"),
6192        }
6193    }
6194
6195    // ── ProcessSpec::gate_compute_defaults substrate pins ───────────────
6196    //
6197    // The 12-line `ProcessSpec { identity: <Default>, classification:
6198    // Classification::gate_compute(), intent: <Default>, boundary:
6199    // Default::default(), compliance: Default::default(), depends_on:
6200    // vec![], signals: Default::default(), lifetime: Default::default(),
6201    // routing: None, encapsulates: None, suspended: false }` struct-
6202    // literal was open-coded verbatim at eight hand-authored callsites
6203    // before this primitive closed it. These pins bind the composed
6204    // shape at fail-before-pass-after granularity so a regression that
6205    // drifted the classification baseline, promoted a defaulted slot to
6206    // a non-default, or leaked a non-baseline slot into the substrate
6207    // composer surfaces HERE rather than as silent operator-visible
6208    // drift across every test fixture that keys assertions on the
6209    // shape.
6210    fn hand_authored_pre_lift() -> ProcessSpec {
6211        ProcessSpec {
6212            identity: IdentitySpec::default(),
6213            classification: Classification::gate_compute(),
6214            intent: Intent::default(),
6215            boundary: Default::default(),
6216            compliance: Default::default(),
6217            depends_on: vec![],
6218            signals: Default::default(),
6219            lifetime: Default::default(),
6220            routing: None,
6221            encapsulates: None,
6222            suspended: false,
6223        }
6224    }
6225
6226    #[test]
6227    fn gate_compute_defaults_composes_the_classification_baseline() {
6228        // Primary shape: the classification axis rides the sibling
6229        // `Classification::gate_compute` primitive verbatim. A
6230        // regression that flipped the classification baseline (a new
6231        // `#[default]` on the sibling closed-set, a re-import through a
6232        // different composer) surfaces HERE rather than at every
6233        // downstream fixture whose assertions key on
6234        // `spec.classification`.
6235        let s = ProcessSpec::gate_compute_defaults();
6236        assert_eq!(s.classification, Classification::gate_compute());
6237    }
6238
6239    #[test]
6240    fn gate_compute_defaults_defaulted_slots_ride_sibling_defaults() {
6241        // Pins the sibling-default correspondence the doc comment
6242        // names — a regression that promoted any defaulted slot to a
6243        // non-default (a new `#[default]` on `Intent`, a `Lifetime`
6244        // baseline shift, a per-field overlay stamping through the
6245        // primitive) would move the baseline HERE rather than at every
6246        // downstream consumer.
6247        let s = ProcessSpec::gate_compute_defaults();
6248        assert_eq!(
6249            serde_json::to_value(&s.identity).unwrap(),
6250            serde_json::to_value(IdentitySpec::default()).unwrap()
6251        );
6252        assert_eq!(
6253            serde_json::to_value(&s.intent).unwrap(),
6254            serde_json::to_value(Intent::default()).unwrap()
6255        );
6256        assert_eq!(
6257            serde_json::to_value(&s.boundary).unwrap(),
6258            serde_json::to_value(Boundary::default()).unwrap()
6259        );
6260        assert_eq!(
6261            serde_json::to_value(&s.compliance).unwrap(),
6262            serde_json::to_value(ComplianceSpec::default()).unwrap()
6263        );
6264        assert!(s.depends_on.is_empty());
6265        assert_eq!(
6266            serde_json::to_value(&s.signals).unwrap(),
6267            serde_json::to_value(SignalPolicy::default()).unwrap()
6268        );
6269        assert_eq!(
6270            serde_json::to_value(&s.lifetime).unwrap(),
6271            serde_json::to_value(Lifetime::default()).unwrap()
6272        );
6273        assert!(s.routing.is_none());
6274        assert!(s.encapsulates.is_none());
6275        assert!(!s.suspended);
6276    }
6277
6278    #[test]
6279    fn gate_compute_defaults_matches_hand_authored_pre_lift_bytewise() {
6280        // Byte-identical parity pin between the substrate primitive
6281        // and the pre-lift 12-line struct-literal that recurred at
6282        // eight hand-authored sites. Compares via `serde_json` value
6283        // equality — `ProcessSpec` does not derive `PartialEq` (the
6284        // typed fields it composes over do not uniformly derive it),
6285        // so a serialize round-trip is the shape-equality currency the
6286        // pin family already uses for `ProcessSpec`-shaped assertions
6287        // elsewhere in this test module. A regression that reshaped
6288        // the primitive would diverge from the pre-lift block HERE
6289        // rather than at every downstream fixture that keys on the
6290        // shape.
6291        let composed = ProcessSpec::gate_compute_defaults();
6292        let hand_authored = hand_authored_pre_lift();
6293        assert_eq!(
6294            serde_json::to_value(&composed).unwrap(),
6295            serde_json::to_value(&hand_authored).unwrap(),
6296        );
6297    }
6298
6299    #[test]
6300    fn gate_compute_defaults_supports_struct_update_override() {
6301        // The five override sites (three `render.rs` fixtures + two
6302        // `lifetime_clock.rs` fixtures) rely on struct-update syntax
6303        // to override a single slot while the primitive supplies the
6304        // other eleven. Pin the composition here so a regression that
6305        // broke the struct-update path (e.g. a `#[non_exhaustive]`
6306        // attribute added to `ProcessSpec` that would refuse struct-
6307        // update syntax across crate boundaries) surfaces at compile
6308        // time HERE rather than as a five-site downstream break.
6309        let base = ProcessSpec::gate_compute_defaults();
6310        let overridden = ProcessSpec {
6311            suspended: true,
6312            ..ProcessSpec::gate_compute_defaults()
6313        };
6314        assert!(!base.suspended);
6315        assert!(overridden.suspended);
6316        // Every other slot rides the same default as the base.
6317        assert_eq!(
6318            serde_json::to_value(&overridden.classification).unwrap(),
6319            serde_json::to_value(&base.classification).unwrap(),
6320        );
6321        assert_eq!(
6322            serde_json::to_value(&overridden.lifetime).unwrap(),
6323            serde_json::to_value(&base.lifetime).unwrap(),
6324        );
6325    }
6326
6327    #[test]
6328    fn gate_compute_defaults_is_call_time_construction_not_a_shared_singleton() {
6329        // Two independent calls produce structurally-equal but
6330        // distinct values — pins that the primitive is a plain
6331        // constructor rather than a `lazy_static` clone whose in-
6332        // place mutation at one consumer would silently mutate the
6333        // shape at every other consumer. Mirrors the sibling
6334        // `gate_compute_is_call_time_construction_not_a_shared_singleton`
6335        // pin on `Classification::gate_compute`.
6336        let a = ProcessSpec::gate_compute_defaults();
6337        let b = ProcessSpec::gate_compute_defaults();
6338        assert_eq!(
6339            serde_json::to_value(&a).unwrap(),
6340            serde_json::to_value(&b).unwrap(),
6341        );
6342        assert!(!std::ptr::eq(&a, &b));
6343    }
6344
6345    // ─── ProcessSpec::gate_compute_with_axis substrate pins ─────────
6346    //
6347    // Fail-before-pass-after granularity: `gate_compute_with_axis` on
6348    // `ProcessSpec` did not exist before this commit — the
6349    // `for populated in <ClosedSet>::ALL { let mut spec = ProcessSpec::
6350    // gate_compute_defaults(); spec.classification.<axis> = populated; … }`
6351    // 2-line loop-body construction shape recurred at FIFTEEN hand-
6352    // authored callsites inside `tatara-reconciler/src/bin/tatara-check.
6353    // rs` per-axis `evaluate_point_require_tag` probes past the ★★
6354    // PRIME-DIRECTIVE ≥ 2 duplication trigger inside one workspace
6355    // binary. Post-lift the shape lives at ONE substrate primitive that
6356    // hands a fresh [`Self::gate_compute_defaults`] carrier to the SAME
6357    // trait dispatch [`Classification::gate_compute_with_axis`] uses for
6358    // the (Classification-slice × axis-slice) construction, and every
6359    // downstream loop-body fixture that binds through this primitive
6360    // dispatches its per-iteration axis mutation through
6361    // [`ClassificationAxis::overlay`] rather than by directly poking
6362    // `spec.classification.<axis>`. The pins below fence the primitive's
6363    // contract:
6364    // (1) feeding the axis-baseline variant reconstructs
6365    //     `gate_compute_defaults` byte-for-byte on `classification` and
6366    //     every other spec slot, so the overlay is the IDENTITY under
6367    //     baseline input;
6368    // (2) at every axis, feeding a variant mutates ONLY that axis slot
6369    //     on `spec.classification` and leaves the other four axis slots
6370    //     AND every non-classification spec slot at their baseline;
6371    // (3) the primitive delegates to the sibling
6372    //     [`Classification::gate_compute_with_axis`] under the same axis
6373    //     input, so a regression that stomped a `ClassificationAxis`
6374    //     impl at either owner surfaces at both pin sets simultaneously.
6375    // A regression that (a) hijacked the composer to overlay onto a
6376    // stale carrier (a shared static, a call-time `Default::default()`),
6377    // (b) crossed the wires between the five per-axis
6378    // `ClassificationAxis` impls at only one owner (drifting the
6379    // per-spec composer away from the per-classification composer), or
6380    // (c) drifted a non-classification spec slot away from its
6381    // `gate_compute_defaults` baseline on axis overlay would surface at
6382    // ONE substrate primitive rather than at each of the fifteen
6383    // downstream loop-body callsites.
6384
6385    #[test]
6386    fn gate_compute_with_axis_is_identity_under_axis_baseline_input() {
6387        // For each axis, feeding the baseline-of-that-axis variant
6388        // reconstructs exactly `gate_compute_defaults()`. On the two
6389        // axes with no `Default` (`ConvergencePointType`,
6390        // `SubstrateType`) the baseline is the `gate_compute` chosen
6391        // value (`Gate`, `Compute`); on the three defaulted axes the
6392        // baseline is the sibling closed-set `#[default]` (`Bounded`,
6393        // `Monotone`, `Internal`). Mirrors the sibling
6394        // `gate_compute_with_axis_is_identity_under_axis_baseline_input`
6395        // pin on `Classification::gate_compute_with_axis`.
6396        let baseline = ProcessSpec::gate_compute_defaults();
6397        for spec in [
6398            ProcessSpec::gate_compute_with_axis(HorizonKind::Bounded),
6399            ProcessSpec::gate_compute_with_axis(CalmClassification::Monotone),
6400            ProcessSpec::gate_compute_with_axis(DataClassification::Internal),
6401            ProcessSpec::gate_compute_with_axis(ConvergencePointType::Gate),
6402            ProcessSpec::gate_compute_with_axis(SubstrateType::Compute),
6403        ] {
6404            assert_eq!(
6405                serde_json::to_value(&spec).unwrap(),
6406                serde_json::to_value(&baseline).unwrap(),
6407            );
6408        }
6409    }
6410
6411    #[test]
6412    fn gate_compute_with_axis_mutates_only_the_named_axis_slot_on_classification() {
6413        // For each axis, sweep every variant and pin that the four
6414        // sibling axis slots on `spec.classification` stay at their
6415        // `gate_compute` baseline while only the named axis slot
6416        // carries the swept variant. A regression that crossed the
6417        // per-axis impls (a `ClassificationAxis for HorizonKind` body
6418        // that mutated `c.calm` instead of `c.horizon.kind`, or a swap
6419        // between `data_classification` and `calm` impls) fails HERE.
6420        let baseline = Classification::gate_compute();
6421        for populated in HorizonKind::ALL {
6422            let s = ProcessSpec::gate_compute_with_axis(populated);
6423            assert_eq!(s.classification.horizon.kind, populated);
6424            assert_eq!(s.classification.calm, baseline.calm);
6425            assert_eq!(
6426                s.classification.data_classification,
6427                baseline.data_classification
6428            );
6429            assert_eq!(s.classification.point_type, baseline.point_type);
6430            assert_eq!(s.classification.substrate, baseline.substrate);
6431        }
6432        for populated in CalmClassification::ALL {
6433            let s = ProcessSpec::gate_compute_with_axis(populated);
6434            assert_eq!(s.classification.calm, populated);
6435            assert_eq!(s.classification.horizon, baseline.horizon);
6436            assert_eq!(
6437                s.classification.data_classification,
6438                baseline.data_classification
6439            );
6440            assert_eq!(s.classification.point_type, baseline.point_type);
6441            assert_eq!(s.classification.substrate, baseline.substrate);
6442        }
6443        for populated in DataClassification::ALL {
6444            let s = ProcessSpec::gate_compute_with_axis(populated);
6445            assert_eq!(s.classification.data_classification, populated);
6446            assert_eq!(s.classification.horizon, baseline.horizon);
6447            assert_eq!(s.classification.calm, baseline.calm);
6448            assert_eq!(s.classification.point_type, baseline.point_type);
6449            assert_eq!(s.classification.substrate, baseline.substrate);
6450        }
6451        for populated in ConvergencePointType::ALL {
6452            let s = ProcessSpec::gate_compute_with_axis(populated);
6453            assert_eq!(s.classification.point_type, populated);
6454            assert_eq!(s.classification.horizon, baseline.horizon);
6455            assert_eq!(s.classification.calm, baseline.calm);
6456            assert_eq!(
6457                s.classification.data_classification,
6458                baseline.data_classification
6459            );
6460            assert_eq!(s.classification.substrate, baseline.substrate);
6461        }
6462        for populated in SubstrateType::ALL {
6463            let s = ProcessSpec::gate_compute_with_axis(populated);
6464            assert_eq!(s.classification.substrate, populated);
6465            assert_eq!(s.classification.horizon, baseline.horizon);
6466            assert_eq!(s.classification.calm, baseline.calm);
6467            assert_eq!(
6468                s.classification.data_classification,
6469                baseline.data_classification
6470            );
6471            assert_eq!(s.classification.point_type, baseline.point_type);
6472        }
6473        // Sixth-axis-peer coverage — [`OptimizationDirection`] rides
6474        // the nested `Horizon.direction: Option<_>` sub-slot, distinct
6475        // from the direct-scalar four axes above. The nested-Option
6476        // overlay must NOT stomp the sibling `horizon.kind` scalar the
6477        // baseline defaulted at `Bounded`.
6478        for populated in OptimizationDirection::ALL {
6479            let s = ProcessSpec::gate_compute_with_axis(populated);
6480            assert_eq!(s.classification.horizon.direction, Some(populated));
6481            assert_eq!(s.classification.horizon.kind, baseline.horizon.kind);
6482            assert_eq!(s.classification.calm, baseline.calm);
6483            assert_eq!(
6484                s.classification.data_classification,
6485                baseline.data_classification
6486            );
6487            assert_eq!(s.classification.point_type, baseline.point_type);
6488            assert_eq!(s.classification.substrate, baseline.substrate);
6489        }
6490    }
6491
6492    #[test]
6493    fn gate_compute_with_axis_preserves_every_non_classification_spec_slot_at_baseline() {
6494        // Pin the (spec × axis-slice) composer's carrier discipline:
6495        // the axis overlay MUST NOT leak into a non-classification
6496        // slot on the spec. A regression that misrouted an overlay
6497        // through `spec.intent` / `spec.boundary` / `spec.lifetime` /
6498        // any of the other nine spec slots would surface HERE at ONE
6499        // narrow site rather than as silent drift across the fifteen
6500        // downstream loop-body callsites that key their assertions on
6501        // `evaluate_point_require_tag` against the axis slot.
6502        let baseline = ProcessSpec::gate_compute_defaults();
6503        for populated in ConvergencePointType::ALL {
6504            let s = ProcessSpec::gate_compute_with_axis(populated);
6505            assert_eq!(
6506                serde_json::to_value(&s.identity).unwrap(),
6507                serde_json::to_value(&baseline.identity).unwrap(),
6508            );
6509            assert_eq!(
6510                serde_json::to_value(&s.intent).unwrap(),
6511                serde_json::to_value(&baseline.intent).unwrap(),
6512            );
6513            assert_eq!(
6514                serde_json::to_value(&s.boundary).unwrap(),
6515                serde_json::to_value(&baseline.boundary).unwrap(),
6516            );
6517            assert_eq!(
6518                serde_json::to_value(&s.compliance).unwrap(),
6519                serde_json::to_value(&baseline.compliance).unwrap(),
6520            );
6521            assert!(s.depends_on.is_empty());
6522            assert_eq!(
6523                serde_json::to_value(&s.signals).unwrap(),
6524                serde_json::to_value(&baseline.signals).unwrap(),
6525            );
6526            assert_eq!(
6527                serde_json::to_value(&s.lifetime).unwrap(),
6528                serde_json::to_value(&baseline.lifetime).unwrap(),
6529            );
6530            assert!(s.routing.is_none());
6531            assert!(s.encapsulates.is_none());
6532            assert_eq!(s.suspended, baseline.suspended);
6533        }
6534    }
6535
6536    #[test]
6537    fn gate_compute_with_axis_delegates_to_sibling_classification_composer() {
6538        // Byte-parity pin between the (spec × axis-slice) composer's
6539        // classification output and the sibling (Classification ×
6540        // axis-slice) composer's output under the same axis input.
6541        // Pin every direct-scalar closed-set axis so a regression
6542        // that drifted the per-spec composer away from the per-
6543        // classification composer at ANY axis surfaces HERE at ONE
6544        // narrow site.
6545        for populated in ConvergencePointType::ALL {
6546            let s = ProcessSpec::gate_compute_with_axis(populated);
6547            assert_eq!(
6548                s.classification,
6549                Classification::gate_compute_with_axis(populated),
6550            );
6551        }
6552        for populated in SubstrateType::ALL {
6553            let s = ProcessSpec::gate_compute_with_axis(populated);
6554            assert_eq!(
6555                s.classification,
6556                Classification::gate_compute_with_axis(populated),
6557            );
6558        }
6559        for populated in CalmClassification::ALL {
6560            let s = ProcessSpec::gate_compute_with_axis(populated);
6561            assert_eq!(
6562                s.classification,
6563                Classification::gate_compute_with_axis(populated),
6564            );
6565        }
6566        for populated in DataClassification::ALL {
6567            let s = ProcessSpec::gate_compute_with_axis(populated);
6568            assert_eq!(
6569                s.classification,
6570                Classification::gate_compute_with_axis(populated),
6571            );
6572        }
6573        for populated in HorizonKind::ALL {
6574            let s = ProcessSpec::gate_compute_with_axis(populated);
6575            assert_eq!(
6576                s.classification,
6577                Classification::gate_compute_with_axis(populated),
6578            );
6579        }
6580        for populated in OptimizationDirection::ALL {
6581            let s = ProcessSpec::gate_compute_with_axis(populated);
6582            assert_eq!(
6583                s.classification,
6584                Classification::gate_compute_with_axis(populated),
6585            );
6586        }
6587    }
6588
6589    // ─── ProcessStatus::at_phase substrate pins ─────────────────────
6590    //
6591    // The 3-line `ProcessStatus { phase: <ProcessPhase::…>, ..Default::
6592    // default() }` shape now rides through the ONE substrate composer
6593    // [`ProcessStatus::at_phase`] across the two pool-reconciler
6594    // phase-decision pin sites (`process_to_member_state_attested_
6595    // permanent_is_free`, `process_to_member_state_attested_ephemeral_
6596    // is_allocated`). These pins bind the primitive at fail-before-pass-
6597    // after granularity so a regression that drifted the phase slot
6598    // pass-through, leaked a sibling slot away from `Default`, or
6599    // hijacked the composer to stamp a static `phase_since` /
6600    // `attestation` on the `phase` transition surfaces HERE rather
6601    // than as silent phase-decision skew across the two pool-reconciler
6602    // callsites (or across any future consumer fixture that binds a
6603    // phase-observation shape).
6604
6605    #[test]
6606    fn at_phase_binds_caller_supplied_phase_verbatim_at_the_phase_slot() {
6607        // The composer's `phase` slot is the caller-supplied
6608        // `ProcessPhase` verbatim — no case-fold, no substitution, no
6609        // remapping to a peer variant. Sweep every variant so a
6610        // regression that hijacked one arm to stamp a different variant
6611        // silently would surface here (per-variant coverage matters
6612        // because the pool-reconciler's `process_to_member_state`
6613        // matcher already keys on `ProcessPhase::Attested` specifically,
6614        // and a peer variant lift would need the pass-through to
6615        // faithfully carry any of the eight variants without translation).
6616        for phase in [
6617            ProcessPhase::Pending,
6618            ProcessPhase::Forking,
6619            ProcessPhase::Execing,
6620            ProcessPhase::Running,
6621            ProcessPhase::Reconverging,
6622            ProcessPhase::Attested,
6623            ProcessPhase::Failed,
6624            ProcessPhase::Exiting,
6625        ] {
6626            let s = ProcessStatus::at_phase(phase);
6627            assert_eq!(
6628                s.phase, phase,
6629                "at_phase({phase:?}) must stamp the caller-supplied phase verbatim",
6630            );
6631        }
6632    }
6633
6634    #[test]
6635    fn at_phase_leaves_every_other_slot_at_default_no_sibling_leak() {
6636        // The composer stamps ONLY the `phase` slot — every other slot
6637        // (`pid`, `parent`, `children`, `identity`, `phase_since`,
6638        // `attestation`, `flux_resources`, `boundary`, `compliance`,
6639        // `signal_queue`, `conditions`, `message`, `exit_code`) parks at
6640        // `Default`. A regression that widened the composer's stamped
6641        // slot set (an auto-stamped `phase_since = Utc::now()` overlay
6642        // that would break byte-identical parity with the pre-lift
6643        // 3-line struct-literal, a defaulted-non-empty `flux_resources`
6644        // fixture that would silently reshape every pool-reconciler
6645        // phase-decision test's downstream `.flux_resources` observation)
6646        // surfaces HERE at the pin block rather than as silent skew
6647        // at every fixture consumer.
6648        let s = ProcessStatus::at_phase(ProcessPhase::Attested);
6649        assert!(s.pid.is_none(), "pid parks at Default (None)");
6650        assert!(s.parent.is_none(), "parent parks at Default (None)");
6651        assert!(
6652            s.children.is_empty(),
6653            "children parks at Default (Vec::new())"
6654        );
6655        assert!(s.identity.is_none(), "identity parks at Default (None)");
6656        assert!(
6657            s.phase_since.is_none(),
6658            "phase_since parks at Default (None) — a call-time Utc::now() stamp would break \
6659             byte-identical parity with the pre-lift `..Default::default()` struct-update shape",
6660        );
6661        assert!(
6662            s.attestation.is_none(),
6663            "attestation parks at Default (None)"
6664        );
6665        assert!(
6666            s.flux_resources.is_empty(),
6667            "flux_resources parks at Default (Vec::new())",
6668        );
6669        assert_eq!(
6670            serde_json::to_value(&s.boundary).unwrap(),
6671            serde_json::to_value(BoundaryStatus::default()).unwrap(),
6672            "boundary parks at Default",
6673        );
6674        assert_eq!(
6675            serde_json::to_value(&s.compliance).unwrap(),
6676            serde_json::to_value(ComplianceStatus::default()).unwrap(),
6677            "compliance parks at Default",
6678        );
6679        assert!(
6680            s.signal_queue.is_empty(),
6681            "signal_queue parks at Default (Vec::new())",
6682        );
6683        assert!(
6684            s.conditions.is_empty(),
6685            "conditions parks at Default (Vec::new())"
6686        );
6687        assert!(s.message.is_none(), "message parks at Default (None)");
6688        assert!(s.exit_code.is_none(), "exit_code parks at Default (None)");
6689    }
6690
6691    #[test]
6692    fn at_phase_matches_hand_authored_pre_lift_bytewise() {
6693        // Byte-identical parity pin between the substrate composer and
6694        // the pre-lift 3-line `ProcessStatus { phase: <p>, ..Default::
6695        // default() }` struct-literal that recurred at both pool-
6696        // reconciler pin sites. Compares via `serde_json` value
6697        // equality — `ProcessStatus` does not derive `PartialEq` (the
6698        // typed fields it composes over do not uniformly derive it),
6699        // so a serialize round-trip is the shape-equality currency the
6700        // pin family already uses for status-shaped assertions in this
6701        // module (see the sibling `gate_compute_defaults_matches_hand_
6702        // authored_pre_lift_bytewise` pin on the spec side). A
6703        // regression that reshaped the primitive would diverge from
6704        // the pre-lift struct-literal HERE rather than at every
6705        // downstream fixture that keys on the shape.
6706        for phase in [
6707            ProcessPhase::Attested,
6708            ProcessPhase::Running,
6709            ProcessPhase::Pending,
6710        ] {
6711            let composed = ProcessStatus::at_phase(phase);
6712            let hand_authored = ProcessStatus {
6713                phase,
6714                ..Default::default()
6715            };
6716            assert_eq!(
6717                serde_json::to_value(&composed).unwrap(),
6718                serde_json::to_value(&hand_authored).unwrap(),
6719                "primitive must be byte-identical to the pre-lift struct-literal for phase {phase:?}",
6720            );
6721        }
6722    }
6723
6724    #[test]
6725    fn at_phase_is_call_time_construction_not_a_shared_singleton() {
6726        // Two independent calls produce structurally-equal but distinct
6727        // values — pins that the primitive is a plain constructor
6728        // rather than a `lazy_static` clone whose in-place mutation at
6729        // one consumer would silently mutate the shape at every other
6730        // consumer. Mirrors the sibling
6731        // `gate_compute_defaults_is_call_time_construction_not_a_shared_singleton`
6732        // pin on `ProcessSpec::gate_compute_defaults`.
6733        let a = ProcessStatus::at_phase(ProcessPhase::Attested);
6734        let b = ProcessStatus::at_phase(ProcessPhase::Attested);
6735        assert_eq!(
6736            serde_json::to_value(&a).unwrap(),
6737            serde_json::to_value(&b).unwrap(),
6738        );
6739        assert!(!std::ptr::eq(&a, &b));
6740    }
6741
6742    #[test]
6743    fn at_phase_default_variant_equals_process_status_default() {
6744        // Handing the composer the `ProcessPhase::default()` variant
6745        // yields a value byte-identical to `ProcessStatus::default()`
6746        // itself — pins that the composer's ONLY divergence from
6747        // `Default` is the caller-supplied `phase` slot, and that when
6748        // the caller passes the same variant `phase` already defaults
6749        // to, the composer collapses cleanly to the plain default.
6750        // A regression that stamped a non-default value on any sibling
6751        // slot (a runtime timestamp on `phase_since`, a synthetic
6752        // `identity` seed) would break this collapse and surface HERE.
6753        let default_phase = ProcessPhase::default();
6754        let via_at_phase = ProcessStatus::at_phase(default_phase);
6755        let via_default = ProcessStatus::default();
6756        assert_eq!(
6757            serde_json::to_value(&via_at_phase).unwrap(),
6758            serde_json::to_value(&via_default).unwrap(),
6759        );
6760    }
6761
6762    // ─── Process::owned_name_and_uid_or_err substrate pins ─────────────
6763    //
6764    // Fail-before-pass-after granularity: the
6765    // `Process::owned_name_and_uid_or_err` method did not exist before
6766    // this commit, so each test below fails to compile pre-lift.
6767    // Post-lift they collectively pin the paired 2-slot required-
6768    // extract shape at ONE substrate owner — a regression that swapped
6769    // the two gates (uid before name), drifted the wire-form back to
6770    // the pre-lift `tatara-reconciler::ssapply::build_owner_reference`
6771    // lowercase-verb spelling (`"process missing metadata.name"`),
6772    // relaxed either gate to a silent-string fallback (an `unwrap_or_default`
6773    // that would silently propagate as an orphan owner reference at
6774    // `owner_references_json`'s empty-uid `is_empty` gate), or reshaped
6775    // the axis order of the return tuple (`(uid, name)` — bytewise
6776    // wrong at the `owner_reference_json(name, uid)` positional-arg
6777    // consumer) surfaces HERE rather than as silent operator-facing
6778    // drift at the pre-lift ssapply consumer whose downstream fed
6779    // `owner_reference_json` positionally.
6780
6781    #[test]
6782    fn owned_name_and_uid_or_err_returns_owned_pair_when_both_slots_present() {
6783        // Happy-path pin: both slots populated — method returns owned
6784        // `String`s in `(name, uid)` axis order (matches
6785        // `owner_reference_json(name, uid)` positional-arg order).
6786        let mut p = Process::new("api-gateway", empty_spec());
6787        p.metadata.uid = Some("uid-abc-123".into());
6788        let (name, uid) = p.owned_name_and_uid_or_err().unwrap();
6789        assert_eq!(name, "api-gateway");
6790        assert_eq!(uid, "uid-abc-123");
6791        // Ownership pin: type inference above binds name/uid as owned
6792        // Strings — a regression that returned `&str` would fail to
6793        // compile at the following .push_str() call. Holds the "owned"
6794        // half of the primitive's contract.
6795        let mut owned_uid = uid;
6796        owned_uid.push_str("-mutated");
6797        assert_eq!(owned_uid, "uid-abc-123-mutated");
6798    }
6799
6800    #[test]
6801    fn owned_name_and_uid_or_err_errors_when_metadata_uid_absent() {
6802        // Uid-gate pin: name populated (via `Process::new`), uid
6803        // absent → Err mentioning `metadata.uid`. Load-bearing at
6804        // the pre-lift `ssapply::build_owner_reference` caller whose
6805        // downstream `owner_reference_json` cannot compose without
6806        // both slots.
6807        let p = Process::new("some-proc", empty_spec());
6808        // Process::new leaves metadata.uid = None by default.
6809        let err = p.owned_name_and_uid_or_err().unwrap_err();
6810        assert_eq!(err.to_string(), "Process has no metadata.uid");
6811    }
6812
6813    #[test]
6814    fn owned_name_and_uid_or_err_errors_when_metadata_name_absent() {
6815        // Name-gate pin: name absent → Err mentioning `metadata.name`.
6816        // The name gate fires FIRST — see the paired ordering pin
6817        // below for the missing-both corner.
6818        let mut p = Process::new("scratch", empty_spec());
6819        p.metadata.name = None;
6820        p.metadata.uid = Some("uid-42".into());
6821        let err = p.owned_name_and_uid_or_err().unwrap_err();
6822        assert_eq!(err.to_string(), "Process has no metadata.name");
6823    }
6824
6825    #[test]
6826    fn owned_name_and_uid_or_err_reports_name_first_when_both_slots_absent() {
6827        // Ordering pin: on a `Process` fixture missing BOTH slots the
6828        // returned error names `metadata.name` (matches how
6829        // `owned_coordinates_or_err` orders its two gates on the
6830        // sibling `(namespace, name)` primitive — the "first-missing-
6831        // slot" slug the family surfaces at the paired missing-both
6832        // corner is consistently the FIRST gate). A regression that
6833        // swapped the two gates would flip the reported slug and
6834        // surface HERE rather than as a subtle wire-form drift in
6835        // operator alerts bisecting a "which slot is missing" fault.
6836        let mut p = Process::new("scratch", empty_spec());
6837        p.metadata.name = None;
6838        p.metadata.uid = None;
6839        let err = p.owned_name_and_uid_or_err().unwrap_err();
6840        assert_eq!(err.to_string(), "Process has no metadata.name");
6841    }
6842
6843    #[test]
6844    fn owned_name_and_uid_or_err_wire_form_matches_owned_coordinates_or_err_family_spelling() {
6845        // Cross-primitive wire-form coherence pin — BOTH gates of this
6846        // method's error output use the workspace-canonical
6847        // `"Process has no metadata.<slot>"` spelling
6848        // `Self::owned_coordinates_or_err` pins in the family. A
6849        // regression that reverted either gate to the pre-lift
6850        // `ssapply::build_owner_reference` lowercase-verb spelling
6851        // (`"process missing metadata.<slot>"`) would reopen a
6852        // workspace-wide operator-facing wire-form drift the lift
6853        // closed, and surface HERE rather than as silent
6854        // divergence between the two owned-required-extract primitives
6855        // in the family (operators bisecting a "which slot faulted"
6856        // alert see a mixed-case grep footprint when the two are out
6857        // of sync).
6858        let mut p_name_absent = Process::new("s", empty_spec());
6859        p_name_absent.metadata.name = None;
6860        assert_eq!(
6861            p_name_absent
6862                .owned_name_and_uid_or_err()
6863                .unwrap_err()
6864                .to_string(),
6865            "Process has no metadata.name",
6866        );
6867        let p_uid_absent = Process::new("s", empty_spec());
6868        // `Process::new` leaves metadata.uid = None.
6869        assert_eq!(
6870            p_uid_absent
6871                .owned_name_and_uid_or_err()
6872                .unwrap_err()
6873                .to_string(),
6874            "Process has no metadata.uid",
6875        );
6876    }
6877
6878    #[test]
6879    fn owned_name_and_uid_or_err_matches_pre_lift_reconciler_helper_shape() {
6880        // Byte-identical parity pin between the paired required-extract
6881        // primitive here and the pre-lift
6882        // `tatara-reconciler::ssapply::build_owner_reference` helper
6883        // shape — the exact 2-slot unwrap chain the pre-lift caller
6884        // spelled by hand (with the wire-form updated from the pre-lift
6885        // lowercase-verb spelling to the workspace-canonical
6886        // `owned_coordinates_or_err`-family spelling — the intentional
6887        // wire-form drift-close per the primitive's docs).
6888        //
6889        // Sweeps every corner every callsite plausibly encounters
6890        // (both slots present, uid absent, name absent, both absent).
6891        // A regression that inserted a normalization step at the
6892        // primitive that the pre-lift chain does NOT apply — or vice
6893        // versa — surfaces here rather than as silent drift between
6894        // the pre-lift consumer callsite and the ONE substrate owner
6895        // it now routes through.
6896        fn pre_lift(p: &Process) -> anyhow::Result<(String, String)> {
6897            let name = p
6898                .metadata
6899                .name
6900                .clone()
6901                .ok_or_else(|| anyhow::anyhow!("Process has no metadata.name"))?;
6902            let uid = p
6903                .metadata
6904                .uid
6905                .clone()
6906                .ok_or_else(|| anyhow::anyhow!("Process has no metadata.uid"))?;
6907            Ok((name, uid))
6908        }
6909        // Both present.
6910        let mut p = Process::new("api", empty_spec());
6911        p.metadata.uid = Some("uid-1".into());
6912        assert_eq!(
6913            p.owned_name_and_uid_or_err().unwrap(),
6914            pre_lift(&p).unwrap()
6915        );
6916        // Uid absent (name populated by Process::new).
6917        let p = Process::new("api", empty_spec());
6918        assert_eq!(
6919            p.owned_name_and_uid_or_err().unwrap_err().to_string(),
6920            pre_lift(&p).unwrap_err().to_string(),
6921        );
6922        // Name absent, uid present.
6923        let mut p = Process::new("api", empty_spec());
6924        p.metadata.name = None;
6925        p.metadata.uid = Some("uid-1".into());
6926        assert_eq!(
6927            p.owned_name_and_uid_or_err().unwrap_err().to_string(),
6928            pre_lift(&p).unwrap_err().to_string(),
6929        );
6930        // Both absent — the name gate fires first at both routes.
6931        let mut p = Process::new("api", empty_spec());
6932        p.metadata.name = None;
6933        p.metadata.uid = None;
6934        assert_eq!(
6935            p.owned_name_and_uid_or_err().unwrap_err().to_string(),
6936            pre_lift(&p).unwrap_err().to_string(),
6937        );
6938    }
6939
6940    #[test]
6941    fn owned_name_and_uid_or_err_axis_order_matches_owner_reference_json_positional_args() {
6942        // Cross-substrate composition pin — the `(name, uid)` tuple
6943        // this primitive returns MUST feed
6944        // `crate::owner_reference_json(name, uid)` positionally without
6945        // an axis-swap step. A regression that reshaped the return
6946        // tuple to `(uid, name)` would type-check silently (both
6947        // arguments are `&str`) but produce a bytewise wrong owner
6948        // reference whose `name` slot carried the uid string. Load-
6949        // bearing at every downstream K8s apiserver reader of the
6950        // stamped OwnerReference (garbage-collector cascade-delete
6951        // fan-out, the `KUBECTL get -o wide` output an operator
6952        // inspects, every controller reconciling a downstream-owned
6953        // resource).
6954        let mut p = Process::new("owner-name", empty_spec());
6955        p.metadata.uid = Some("owner-uid".into());
6956        let (name, uid) = p.owned_name_and_uid_or_err().unwrap();
6957        let owner_ref = crate::owner_reference_json(&name, &uid);
6958        assert_eq!(owner_ref["name"], "owner-name");
6959        assert_eq!(owner_ref["uid"], "owner-uid");
6960    }
6961}