Skip to main content

tatara_process/
classification.rs

1//! The six classification dimensions — CRD-facing with `JsonSchema`,
2//! `From`/`Into` bridges to `tatara_core::domain::classification`.
3
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7use tatara_core::domain::classification as core;
8use tatara_core::domain::compliance_binding as core_compl;
9
10/// Lattice position of a Process — six orthogonal axes.
11#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
12#[serde(rename_all = "camelCase")]
13pub struct Classification {
14    pub point_type: ConvergencePointType,
15    pub substrate: SubstrateType,
16    #[serde(default)]
17    pub horizon: Horizon,
18    #[serde(default)]
19    pub calm: CalmClassification,
20    #[serde(default)]
21    pub data_classification: DataClassification,
22}
23
24impl Classification {
25    /// The workspace-baseline classification — a [`ConvergencePointType::Gate`]
26    /// point on the [`SubstrateType::Compute`] substrate with every other axis
27    /// at its [`Default`]. The `(Gate, Compute)` pair names an unremarkable
28    /// barrier point in the Compute plane: no domain-specific structural
29    /// claim (no fan-out / fan-in / broadcast / observation semantics beyond
30    /// the barrier gate) and no domain-specific substrate claim (no
31    /// `Financial` / `Network` / `Storage` / `Security` / `Identity` /
32    /// `Observability` / `Regulatory` plane bringing in its own compliance
33    /// baselines). The three defaulted axes ride at the intentional
34    /// workspace baseline the sibling closed-set primitives already own:
35    /// [`Horizon`] at [`HorizonKind::Bounded`] (terminates naturally, no
36    /// asymptotic metric axes required), [`CalmClassification::Monotone`]
37    /// (no coordination required per CALM), and
38    /// [`DataClassification::Internal`] (access-controlled but not
39    /// externally regulated).
40    ///
41    /// Pre-lift the six-line `Classification { point_type: Gate, substrate:
42    /// Compute, horizon: Default::default(), calm: Default::default(),
43    /// data_classification: Default::default() }` struct-literal recurred
44    /// at TEN sites past the ★★ PRIME-DIRECTIVE ≥ 2 duplication threshold
45    /// — one production consumer plus nine test-fixture callsites spread
46    /// across four crates, each restating the SAME `(Gate, Compute)`
47    /// baseline verbatim:
48    /// * `crate::ephemeral::default_ephemeral_class` — the substitute
49    ///   [`EphemeralSpec::into::<crate::crd::ProcessSpec>`] fills into
50    ///   [`crate::crd::ProcessSpec::classification`] when the operator
51    ///   omits an explicit `:classification` slot on `(defephemeral …)`.
52    ///   The one PRODUCTION consumer of the shape — a regression that
53    ///   drifted its point-type or substrate axis silently retargets every
54    ///   unadorned ephemeral to a different plane.
55    /// * `crate::crd`'s + `crate::lib`'s + `crate::lifetime_clock`'s +
56    ///   `tatara_reconciler::{claim,render}`'s + `tatara_pool_reconciler::
57    ///   controller_pool`'s `empty_spec` / `empty_process_spec` /
58    ///   `ephemeral_process` / `permanent_process` test-fixture helpers +
59    ///   inline `ProcessSpec` literals — nine test-fixture callsites
60    ///   restating the SAME six-line struct-literal at the same shape.
61    ///
62    /// Post-lift every callsite reads `Classification::gate_compute()`;
63    /// a future workspace-wide baseline shift (a new [`Horizon`] default,
64    /// a promotion of `Compute` to a compound baseline that pre-fills a
65    /// canonical [`CalmClassification`], a per-baseline compliance overlay
66    /// stamping through the classification, or a rename of either axis
67    /// enum) lands at ONE substrate function here and every downstream
68    /// consumer inherits the upgrade mechanically. The current pin ties
69    /// the three defaulted axes to the sibling closed-set defaults
70    /// ([`HorizonKind::Bounded`], [`CalmClassification::Monotone`],
71    /// [`DataClassification::Internal`]) so a future change to any sibling
72    /// default surfaces at this primitive's tests rather than as silent
73    /// drift across ten independent callsites.
74    ///
75    /// Sibling to the `_or_default` / `_or_placeholder` primitive family on
76    /// [`crate::prelude::Process`] on the (return-form × axis) axis — those
77    /// primitives own the borrow-form projections off a live `Process`;
78    /// this one owns the construction shape for a fresh
79    /// [`crate::crd::ProcessSpec`] whose classification axis is
80    /// unremarkable. A future peer `Classification::observe_observability()`
81    /// or similar named variant lands as a sibling method here when a
82    /// second unremarkable-baseline shape opens.
83    ///
84    /// Theory anchor: THEORY.md §VI.1 (generation over composition — the
85    /// six-line struct-literal shape recurred at TEN hand-authored sites
86    /// past the ★★ PRIME-DIRECTIVE ≥ 2 duplication trigger and is lifted
87    /// onto ONE workspace-wide owner here). THEORY.md §II.1 invariant 5
88    /// (composition preserves proofs — a regression that drifted the
89    /// baseline axis choice at only one consumer, or that broke the
90    /// sibling-default correspondence, surfaces at this primitive's tests
91    /// rather than as silent operator-visible skew between the ephemeral
92    /// sugar substitute and the ten downstream test-fixtures whose
93    /// assertions depend on the shape).
94    #[must_use]
95    pub fn gate_compute() -> Self {
96        Self {
97            point_type: ConvergencePointType::Gate,
98            substrate: SubstrateType::Compute,
99            horizon: Horizon::default(),
100            calm: CalmClassification::default(),
101            data_classification: DataClassification::default(),
102        }
103    }
104
105    /// Closed-set-driven presence probe — does this [`Classification`]
106    /// carry the given [`ConvergencePointType`] discriminator on its
107    /// [`Self::point_type`] slot? The ONE substrate primitive that owns
108    /// the `(Classification, ConvergencePointType) -> bool`
109    /// scalar-carrier walk shape.
110    ///
111    /// # Third scalar-carrier peer on the presence-probe axis
112    ///
113    /// Peer of [`crate::spec::SignalPolicy::has_sighup_strategy`] and
114    /// [`crate::encapsulates::EncapsulatesSpec::has_mode`] — all three
115    /// probe a scalar closed-set-discriminator field on an inner
116    /// [`crate::crd::ProcessSpec`] struct via a one-line
117    /// `self.<field> == kind` body. Together they compose the
118    /// SCALAR-CARRIER stratum of the workspace-wide closed-set-driven
119    /// presence-probe algebra (the workspace-wide algebra spans three
120    /// underlying representation kinds — Option-slot, slice, scalar —
121    /// see the [`crate::spec::SignalPolicy::has_sighup_strategy`]
122    /// docstring for the full-shape rundown; this method is the third
123    /// scalar-carrier instance).
124    ///
125    /// # Semantics — VARIANT match, not POPULATED slot
126    ///
127    /// `has_point_type(kind)` returns `true` iff `self.point_type ==
128    /// kind`. Distinct from BOTH prior scalar-carrier peers on the
129    /// (parent-shape × child-shape) axis:
130    ///
131    /// * [`crate::spec::SignalPolicy::has_sighup_strategy`] lives on a
132    ///   non-Option, DEFAULTED parent (`SignalPolicy: Default`) with a
133    ///   defaulted scalar child (`SighupStrategy: Default =
134    ///   Reconverge`) — a default carrier reads `true` for the default
135    ///   variant only.
136    /// * [`crate::encapsulates::EncapsulatesSpec::has_mode`] lives on
137    ///   an OPTION parent (`spec.encapsulates:
138    ///   Option<EncapsulatesSpec>`) with a defaulted scalar child
139    ///   (`EncapsulationMode: Default = Manage`) — a bare `None`
140    ///   parent reads `false` for every variant.
141    /// * `has_point_type` lives on a REQUIRED, non-Option, NON-DEFAULT
142    ///   parent ([`Classification`] has no `impl Default`) with a
143    ///   NON-DEFAULT scalar child ([`ConvergencePointType`] has no
144    ///   `impl Default`) — every well-formed [`crate::crd::ProcessSpec`]
145    ///   carries a `Classification` whose `point_type` slot is
146    ///   deliberately chosen by the operator, so the probe returns
147    ///   `true` on exactly ONE variant per spec and `false` on the
148    ///   other seven, with no default-arm short-circuit shortcut.
149    ///
150    /// This closes the (required-parent × required-scalar-child) corner
151    /// of the workspace-wide closed-set-driven presence-probe algebra
152    /// at its first substrate primitive.
153    ///
154    /// # Compounding
155    ///
156    /// A future closed-set-discriminator scalar field on
157    /// [`Classification`] (a peer `has_substrate`, `has_calm`,
158    /// `has_data_classification` — the four remaining
159    /// classification-axis closed sets) lands as ONE peer inherent
160    /// method with the same one-line `self.<field> == kind` body and
161    /// routes through the same `strip_and_classify_prefixed_kind::<K,
162    /// _>` shape in `tatara-check`. A future
163    /// [`ConvergencePointType`] variant (a hypothetical `Demux` /
164    /// `Mux` / `Pipeline` for finer topology carving) reaches every
165    /// downstream through ONE `ALL` entry on the closed set with the
166    /// probe body untouched.
167    ///
168    /// Theory anchor: THEORY.md §II.1 invariant 5 — composition preserves
169    /// proofs; the scalar-carrier presence-probe body lives at ONE
170    /// substrate site so every downstream (`point-type-<kind>`
171    /// require-tag family in `tatara-check`, closed-set audit
172    /// dispatchers, future variant additions on
173    /// [`ConvergencePointType`]) binds through the SAME shape rather
174    /// than restating the `classification.point_type == kind` closure
175    /// body at each callsite. THEORY.md §VI.1 — generation over
176    /// composition; a future [`ConvergencePointType`] variant lands at
177    /// ONE `ALL` entry + ONE `as_str` arm on the closed set and the
178    /// probe picks it up mechanically without further per-consumer
179    /// edits.
180    #[must_use]
181    pub fn has_point_type(&self, kind: ConvergencePointType) -> bool {
182        self.point_type == kind
183    }
184
185    /// Closed-set-driven presence probe — does this [`Classification`]
186    /// carry the given [`SubstrateType`] discriminator on its
187    /// [`Self::substrate`] slot? The ONE substrate primitive that
188    /// owns the `(Classification, SubstrateType) -> bool`
189    /// scalar-carrier walk shape.
190    ///
191    /// # Fourth scalar-carrier peer on the presence-probe axis
192    ///
193    /// Peer of [`crate::spec::SignalPolicy::has_sighup_strategy`],
194    /// [`crate::encapsulates::EncapsulatesSpec::has_mode`], and
195    /// [`Self::has_point_type`] — all four probe a scalar closed-set-
196    /// discriminator field on an inner [`crate::crd::ProcessSpec`]
197    /// struct via a one-line `self.<field> == kind` body. Together
198    /// they compose the SCALAR-CARRIER stratum of the workspace-wide
199    /// closed-set-driven presence-probe algebra (the workspace-wide
200    /// algebra spans three underlying representation kinds — Option-
201    /// slot, slice, scalar — see the
202    /// [`crate::spec::SignalPolicy::has_sighup_strategy`] docstring
203    /// for the full-shape rundown; this method is the fourth scalar-
204    /// carrier instance).
205    ///
206    /// # Semantics — VARIANT match, not POPULATED slot
207    ///
208    /// `has_substrate(kind)` returns `true` iff `self.substrate ==
209    /// kind`. FIRST co-tenant on the (required-parent × required-
210    /// scalar-child) corner of the algebra with [`Self::has_point_type`]
211    /// — both probe REQUIRED, non-Option, NON-DEFAULT slots on the
212    /// same [`Classification`] parent whose two required axes carry
213    /// no [`Default`] impl, so exactly ONE of the eight [`SubstrateType`]
214    /// variants and exactly ONE of the eight [`ConvergencePointType`]
215    /// variants answer `true` per well-formed [`crate::crd::ProcessSpec`],
216    /// with no default-arm short-circuit shortcut. Distinct from the
217    /// two prior scalar-carrier peers on the (parent-shape × child-
218    /// shape) axis: `has_sighup_strategy` lives on a non-Option,
219    /// DEFAULTED parent ([`crate::spec::SignalPolicy`] carries
220    /// `#[derive(Default)]`) with a defaulted scalar child
221    /// ([`crate::signal::SighupStrategy`] defaults to
222    /// [`crate::signal::SighupStrategy::Reconverge`]); `has_mode`
223    /// lives on an OPTION parent (`spec.encapsulates:
224    /// Option<EncapsulatesSpec>`) with a defaulted scalar child
225    /// ([`crate::encapsulates::EncapsulationMode`] defaults to
226    /// [`crate::encapsulates::EncapsulationMode::Manage`]).
227    ///
228    /// This POPULATES the (required-parent × required-scalar-child)
229    /// corner of the workspace-wide closed-set-driven presence-probe
230    /// algebra at its SECOND substrate primitive after
231    /// [`Self::has_point_type`] opened the corner, pinning the corner
232    /// as a proven-repeatable primitive shape rather than a single-
233    /// example curiosity.
234    ///
235    /// # Compounding
236    ///
237    /// A future closed-set-discriminator scalar field on
238    /// [`Classification`] (a peer `has_calm` on [`CalmClassification`],
239    /// `has_data_classification` on [`DataClassification`] — the two
240    /// remaining defaulted-scalar-child classification-axis closed
241    /// sets) lands as ONE peer inherent method with the same one-line
242    /// `self.<field> == kind` body and routes through the same
243    /// `strip_and_classify_prefixed_kind::<K, _>` shape in
244    /// `tatara-check`. A future [`SubstrateType`] variant (a
245    /// hypothetical `Consensus` for governance substrates, `Physical`
246    /// for hardware substrates, `Cache` for ephemeral memoization
247    /// substrates) reaches every downstream through ONE `ALL` entry
248    /// on the closed set with the probe body untouched.
249    ///
250    /// Theory anchor: THEORY.md §II.1 invariant 5 — composition preserves
251    /// proofs; the scalar-carrier presence-probe body lives at ONE
252    /// substrate site so every downstream (`substrate-<kind>`
253    /// require-tag family in `tatara-check`, closed-set audit
254    /// dispatchers, future variant additions on [`SubstrateType`])
255    /// binds through the SAME shape rather than restating the
256    /// `classification.substrate == kind` closure body at each
257    /// callsite. THEORY.md §VI.1 — generation over composition; a
258    /// future [`SubstrateType`] variant lands at ONE `ALL` entry +
259    /// ONE `as_str` arm on the closed set and the probe picks it up
260    /// mechanically without further per-consumer edits.
261    #[must_use]
262    pub fn has_substrate(&self, kind: SubstrateType) -> bool {
263        self.substrate == kind
264    }
265
266    /// Closed-set-driven presence probe — does this [`Classification`]
267    /// carry the given [`CalmClassification`] discriminator on its
268    /// [`Self::calm`] slot? The ONE substrate primitive that owns the
269    /// `(Classification, CalmClassification) -> bool` scalar-carrier
270    /// walk shape.
271    ///
272    /// # Fifth scalar-carrier peer on the presence-probe axis
273    ///
274    /// Peer of [`crate::spec::SignalPolicy::has_sighup_strategy`],
275    /// [`crate::encapsulates::EncapsulatesSpec::has_mode`],
276    /// [`Self::has_point_type`], and [`Self::has_substrate`] — all
277    /// five probe a scalar closed-set-discriminator field on an inner
278    /// [`crate::crd::ProcessSpec`] struct via a one-line
279    /// `self.<field> == kind` body. Together they compose the
280    /// SCALAR-CARRIER stratum of the workspace-wide closed-set-driven
281    /// presence-probe algebra (the workspace-wide algebra spans three
282    /// underlying representation kinds — Option-slot, slice, scalar
283    /// — see the [`crate::spec::SignalPolicy::has_sighup_strategy`]
284    /// docstring for the full-shape rundown; this method is the
285    /// fifth scalar-carrier instance).
286    ///
287    /// # Semantics — VARIANT match, not POPULATED slot
288    ///
289    /// `has_calm(kind)` returns `true` iff `self.calm == kind`. FIRST
290    /// occupant on a FRESH corner of the (parent-shape × child-shape)
291    /// axis: a REQUIRED, non-Option, NON-DEFAULT parent
292    /// ([`Classification`] has no `impl Default` because its two
293    /// required axes `point_type`/`substrate` carry no default)
294    /// combined with a DEFAULTED scalar child
295    /// ([`CalmClassification`] defaults to
296    /// [`CalmClassification::Monotone`] via `#[default]`). Distinct
297    /// from every prior scalar-carrier peer on the (parent-shape ×
298    /// child-shape) axis:
299    ///
300    /// * [`crate::spec::SignalPolicy::has_sighup_strategy`] lives on
301    ///   a non-Option, DEFAULTED parent
302    ///   ([`crate::spec::SignalPolicy`] carries `#[derive(Default)]`)
303    ///   with a defaulted scalar child
304    ///   ([`crate::signal::SighupStrategy`] defaults to
305    ///   [`crate::signal::SighupStrategy::Reconverge`]) — a bare
306    ///   `SignalPolicy` reads `true` on the default variant only.
307    /// * [`crate::encapsulates::EncapsulatesSpec::has_mode`] lives on
308    ///   an OPTION parent (`spec.encapsulates:
309    ///   Option<EncapsulatesSpec>`) with a defaulted scalar child
310    ///   ([`crate::encapsulates::EncapsulationMode`] defaults to
311    ///   [`crate::encapsulates::EncapsulationMode::Manage`]) — a
312    ///   bare `None` parent reads `false` for every variant.
313    /// * [`Self::has_point_type`] + [`Self::has_substrate`] both live
314    ///   on the REQUIRED, non-Option, NON-DEFAULT [`Classification`]
315    ///   parent with a NON-DEFAULT scalar child — every well-formed
316    ///   [`crate::crd::ProcessSpec`] carries a `Classification` whose
317    ///   corresponding slot was deliberately chosen by the operator,
318    ///   so exactly ONE of the eight variants answers `true` per
319    ///   spec.
320    /// * `has_calm` lives on the REQUIRED, non-Option, NON-DEFAULT
321    ///   [`Classification`] parent with a DEFAULTED scalar child
322    ///   ([`CalmClassification::Monotone`] is the [`Default`] via
323    ///   `#[default]`) — a bare `Classification` filled via
324    ///   `..Default::default()` on the defaulted axes reads `true`
325    ///   for the default variant ([`CalmClassification::Monotone`])
326    ///   and `false` for every other. Exactly ONE of the two variants
327    ///   answers `true` per spec, and the default-arm short-circuit
328    ///   is present (the operator can DECLINE to name the CALM axis
329    ///   and the spec still answers `true` on the default variant).
330    ///
331    /// This OPENS the (required-parent × defaulted-scalar-child)
332    /// corner of the workspace-wide closed-set-driven presence-probe
333    /// algebra at its first substrate primitive — a corner distinct
334    /// from all four prior scalar-carrier peers (which sit on the
335    /// three prior corners: defaulted-parent × defaulted-child,
336    /// Option-parent × defaulted-child, required-parent ×
337    /// required-child).
338    ///
339    /// # Compounding
340    ///
341    /// A future closed-set-discriminator scalar field on
342    /// [`Classification`] whose child carries `#[derive(Default)]`
343    /// (a peer `has_data_classification` on [`DataClassification`],
344    /// whose default is [`DataClassification::Internal`] via
345    /// `#[default]` — the remaining classification-axis closed set
346    /// on a defaulted-scalar-child slot) lands as ONE peer inherent
347    /// method with the same one-line `self.<field> == kind` body and
348    /// routes through the same `strip_and_classify_prefixed_kind::<K,
349    /// _>` shape in `tatara-check`. A future [`CalmClassification`]
350    /// variant (a hypothetical `ConditionallyMonotone` for ops that
351    /// are monotone under a witness, like CRDT joins under a fixed
352    /// schema) reaches every downstream through ONE `ALL` entry on
353    /// the closed set with the probe body untouched.
354    ///
355    /// Theory anchor: THEORY.md §II.1 invariant 5 — composition
356    /// preserves proofs; the scalar-carrier presence-probe body
357    /// lives at ONE substrate site so every downstream
358    /// (`calm-<kind>` require-tag family in `tatara-check`,
359    /// closed-set audit dispatchers, future variant additions on
360    /// [`CalmClassification`]) binds through the SAME shape rather
361    /// than restating the `classification.calm == kind` closure body
362    /// at each callsite. THEORY.md §VI.1 — generation over
363    /// composition; a future [`CalmClassification`] variant lands at
364    /// ONE `ALL` entry + ONE `as_str` arm on the closed set and the
365    /// probe picks it up mechanically without further per-consumer
366    /// edits.
367    #[must_use]
368    pub fn has_calm(&self, kind: CalmClassification) -> bool {
369        self.calm == kind
370    }
371
372    /// Closed-set-driven presence probe — does this [`Classification`]
373    /// carry the given [`DataClassification`] discriminator on its
374    /// [`Self::data_classification`] slot? The ONE substrate primitive
375    /// that owns the `(Classification, DataClassification) -> bool`
376    /// scalar-carrier walk shape.
377    ///
378    /// # Sixth scalar-carrier peer on the presence-probe axis
379    ///
380    /// Peer of [`crate::spec::SignalPolicy::has_sighup_strategy`],
381    /// [`crate::encapsulates::EncapsulatesSpec::has_mode`],
382    /// [`Self::has_point_type`], [`Self::has_substrate`], and
383    /// [`Self::has_calm`] — all six probe a scalar closed-set-
384    /// discriminator field on an inner [`crate::crd::ProcessSpec`]
385    /// struct via a one-line `self.<field> == kind` body. Together
386    /// they compose the SCALAR-CARRIER stratum of the workspace-wide
387    /// closed-set-driven presence-probe algebra (the workspace-wide
388    /// algebra spans three underlying representation kinds — Option-
389    /// slot, slice, scalar — see the
390    /// [`crate::spec::SignalPolicy::has_sighup_strategy`] docstring
391    /// for the full-shape rundown; this method is the sixth scalar-
392    /// carrier instance).
393    ///
394    /// # Semantics — VARIANT match, not POPULATED slot
395    ///
396    /// `has_data_classification(kind)` returns `true` iff
397    /// `self.data_classification == kind`. SECOND co-tenant on the
398    /// (required-parent × defaulted-scalar-child) corner of the
399    /// algebra alongside [`Self::has_calm`] — both probe REQUIRED,
400    /// non-Option, NON-DEFAULT [`Classification`] parent slots with
401    /// a DEFAULTED scalar child ([`DataClassification`] defaults to
402    /// [`DataClassification::Internal`] via `#[default]`, sibling to
403    /// [`CalmClassification::Monotone`]'s `#[default]`), so exactly
404    /// ONE of the six [`DataClassification`] variants answers `true`
405    /// per spec AND the default-arm short-circuit is present (a
406    /// `Classification` filled via `..Default::default()` on the
407    /// `data_classification` axis reads `true` on the default
408    /// variant [`DataClassification::Internal`] and `false` on every
409    /// other).
410    ///
411    /// This POPULATES the (required-parent × defaulted-scalar-child)
412    /// corner of the workspace-wide closed-set-driven presence-probe
413    /// algebra at its SECOND substrate primitive after
414    /// [`Self::has_calm`] opened the corner, pinning the corner as a
415    /// proven-repeatable primitive shape rather than a single-example
416    /// curiosity. The corner-property contract ("bare
417    /// [`Classification`] reads `true` on the default variant")
418    /// now walks TWO independent defaulted-scalar-child slots on the
419    /// SAME [`Classification`] parent — a regression that promoted
420    /// a different [`DataClassification`] variant to `#[default]`
421    /// (or wired the arm to a fixed variant answer) fails HERE at
422    /// ONE narrow substrate site before drifting through every
423    /// unadorned Process's baseline data-classification answer.
424    ///
425    /// # Compounding
426    ///
427    /// This method exhausts the four scalar closed-set-discriminator
428    /// axes on [`Classification`] ([`Self::has_point_type`],
429    /// [`Self::has_substrate`], [`Self::has_calm`], and
430    /// [`Self::has_data_classification`]) — the six-axis classification
431    /// lattice publishes ALL FOUR of its scalar-carrier presence
432    /// probes at ONE substrate site each. The remaining two axes
433    /// (`horizon` — a nested struct threading [`HorizonKind`] through
434    /// `horizon.kind`; the sixth axis is variant-dependent on the
435    /// [`HorizonKind::Asymptotic`] arm) live on nested-struct-scalar
436    /// slots rather than the direct-scalar corner the four current
437    /// peers span — the [`Self::has_horizon_kind`] peer opens that
438    /// fresh (required-parent × nested-struct-scalar-child) corner
439    /// with the same `has(kind)` shape composed through one struct
440    /// hop. A future [`DataClassification`] variant (a
441    /// hypothetical seventh variant beyond `Public / Internal /
442    /// Confidential / Pii / Phi / Pci` — say a `TradeSecret` bucket
443    /// for competitive-sensitive data, or an `Anonymized` bucket for
444    /// pseudonymized-PII whose regulatory posture differs) reaches
445    /// every downstream through ONE `ALL` entry on the closed set +
446    /// ONE `as_str` arm + ONE `sensitivity_rank` arm + one arm per
447    /// predicate with the probe body untouched.
448    ///
449    /// Theory anchor: THEORY.md §II.1 invariant 5 — composition
450    /// preserves proofs; the scalar-carrier presence-probe body
451    /// lives at ONE substrate site so every downstream
452    /// (`data-classification-<kind>` require-tag family in
453    /// `tatara-check`, closed-set audit dispatchers, future variant
454    /// additions on [`DataClassification`]) binds through the SAME
455    /// shape rather than restating the
456    /// `classification.data_classification == kind` closure body at
457    /// each callsite. THEORY.md §VI.1 — generation over composition;
458    /// a future [`DataClassification`] variant lands at ONE `ALL`
459    /// entry + ONE `as_str` arm on the closed set and the probe
460    /// picks it up mechanically without further per-consumer edits.
461    #[must_use]
462    pub fn has_data_classification(&self, kind: DataClassification) -> bool {
463        self.data_classification == kind
464    }
465
466    /// Closed-set-driven presence probe — does this [`Classification`]
467    /// carry the given [`HorizonKind`] discriminator on its
468    /// [`Self::horizon`]`.kind` slot? The ONE substrate primitive that
469    /// owns the `(Classification, HorizonKind) -> bool` nested-struct-
470    /// scalar-carrier walk shape.
471    ///
472    /// # Seventh peer on the presence-probe axis — first on a fresh corner
473    ///
474    /// Peer of [`crate::spec::SignalPolicy::has_sighup_strategy`],
475    /// [`crate::encapsulates::EncapsulatesSpec::has_mode`],
476    /// [`Self::has_point_type`], [`Self::has_substrate`],
477    /// [`Self::has_calm`], and [`Self::has_data_classification`] on the
478    /// workspace-wide closed-set-driven presence-probe algebra — the
479    /// four scalar-carrier peers on [`Classification`] all read a
480    /// closed-set discriminator DIRECTLY off a scalar `Classification`
481    /// slot (`point_type`, `substrate`, `calm`, `data_classification`).
482    /// `has_horizon_kind` instead threads through a NESTED-STRUCT
483    /// intermediary ([`Horizon`], the defaulted nested struct owning
484    /// the `horizon` axis on the six-axis classification lattice) to
485    /// reach a scalar [`HorizonKind`] discriminator on
486    /// `horizon.kind`. This OPENS the (required-parent × nested-
487    /// struct-scalar-child) corner of the algebra at its FIRST
488    /// substrate primitive — a fresh corner distinct from all four
489    /// corner-property-exhaustive scalar-carrier peers on
490    /// [`Classification`].
491    ///
492    /// # Semantics — VARIANT match on the nested scalar, not POPULATED nested struct
493    ///
494    /// `has_horizon_kind(kind)` returns `true` iff
495    /// `self.horizon.kind == kind`. The nested [`Horizon`] struct
496    /// carries [`Default`] via `#[derive(Default)]` and its `kind`
497    /// field defaults to [`HorizonKind::Bounded`] via `#[default]`, so
498    /// a [`Classification`] filled via `..Default::default()` on the
499    /// `horizon` axis reads `true` on the default kind
500    /// [`HorizonKind::Bounded`] and `false` on
501    /// [`HorizonKind::Asymptotic`]. The default-arm short-circuit is
502    /// therefore present at this corner too — but through the extra
503    /// struct hop the peer scalar-carrier peers on the defaulted-
504    /// child corner (`has_calm`, `has_data_classification`) walk
505    /// directly. A regression that dropped `#[default]` on
506    /// [`HorizonKind`], or that replaced `Horizon::default()` in
507    /// [`Classification::gate_compute`] with an explicit non-`Bounded`
508    /// kind, surfaces at this primitive's tests before drifting
509    /// through every unadorned Process's baseline horizon answer.
510    ///
511    /// # Compounding
512    ///
513    /// This method OPENS the (required-parent × nested-struct-scalar-
514    /// child) corner of the workspace-wide closed-set-driven
515    /// presence-probe algebra, distinct from the four corner-property-
516    /// exhaustive scalar-carrier peers on [`Classification`]
517    /// ([`Self::has_point_type`], [`Self::has_substrate`],
518    /// [`Self::has_calm`], [`Self::has_data_classification`]) whose
519    /// bodies read a closed-set discriminator directly off a scalar
520    /// slot. A future co-tenant on this fresh corner (a peer probe on
521    /// another nested-struct's scalar discriminator, e.g. a
522    /// hypothetical `has_optimization_direction` reaching
523    /// `spec.classification.horizon.direction.unwrap_or_default()`, or
524    /// a nested-struct-scalar discriminator on a different `ProcessSpec`
525    /// field's inner struct) lands as ONE peer inherent method with
526    /// the same two-hop `self.<outer>.<inner> == kind` body and routes
527    /// through the same `strip_and_classify_prefixed_kind::<K, _>`
528    /// shape in `tatara-check`. A future [`HorizonKind`] variant (a
529    /// hypothetical `Periodic` sentinel for "terminates on each
530    /// window boundary then re-arms", pre-flagged on the closed set's
531    /// `ALL` docstring) reaches every downstream through ONE `ALL`
532    /// entry + one `as_str` arm + one `terminates` arm + one
533    /// `requires_metric_axes` arm on the closed set with the probe
534    /// body untouched.
535    ///
536    /// Theory anchor: THEORY.md §II.1 invariant 5 — composition
537    /// preserves proofs; the nested-struct-scalar-carrier presence-
538    /// probe body lives at ONE substrate site so every downstream
539    /// (`horizon-<kind>` require-tag family in `tatara-check`, future
540    /// audit dispatchers walking [`HorizonKind::ALL`], future variant
541    /// additions on [`HorizonKind`]) binds through the SAME
542    /// `has(kind)` shape rather than restating the
543    /// `classification.horizon.kind == kind` closure body at each
544    /// callsite. THEORY.md §VI.1 — generation over composition; a
545    /// future [`HorizonKind`] variant lands at ONE `ALL` entry + ONE
546    /// `as_str` arm on the closed set and the probe picks it up
547    /// mechanically without further per-consumer edits.
548    #[must_use]
549    pub fn has_horizon_kind(&self, kind: HorizonKind) -> bool {
550        self.horizon.kind == kind
551    }
552
553    /// Closed-set-driven presence probe — does this [`Classification`]
554    /// carry the given [`OptimizationDirection`] discriminator on its
555    /// [`Self::horizon`]`.direction` slot (with the substrate
556    /// `Option::unwrap_or_default` treating `None` as the closed set's
557    /// `#[default] Minimize`)? The ONE substrate primitive that owns
558    /// the `(Classification, OptimizationDirection) -> bool` nested-
559    /// struct-Option-scalar-carrier walk shape.
560    ///
561    /// # Second occupant on the nested-struct-scalar-child corner —
562    /// the Option-hop co-tenant
563    ///
564    /// Peer of [`Self::has_horizon_kind`] on the (required-parent ×
565    /// nested-struct-scalar-child) corner opened by that method — same
566    /// two-hop composition through the nested defaulted [`Horizon`]
567    /// intermediary, but the inner scalar slot is `direction:
568    /// Option<OptimizationDirection>` (an `Option`-hop past the same
569    /// nested [`Horizon`]) rather than a bare scalar. The corner
570    /// therefore admits BOTH direct nested-scalar shapes ([`Horizon`]
571    /// carries `kind: HorizonKind` directly, [`Self::has_horizon_kind`]
572    /// walks it) AND Option-nested-scalar shapes ([`Horizon`] carries
573    /// `direction: Option<OptimizationDirection>`, this method walks
574    /// it through `Option::unwrap_or_default`), pinning the corner as
575    /// a proven-repeatable primitive shape rather than a single-
576    /// example curiosity. Every prior scalar-carrier peer on
577    /// [`Classification`] (`has_point_type`, `has_substrate`,
578    /// `has_calm`, `has_data_classification`) reads a closed-set
579    /// discriminator DIRECTLY off a scalar `Classification` slot; this
580    /// method (like [`Self::has_horizon_kind`]) threads through the
581    /// nested [`Horizon`] intermediary, and additionally traverses the
582    /// `Option`-slot with `unwrap_or_default` so the operator's
583    /// `:requires (optimization-direction-Minimize)` on an unadorned
584    /// baseline still answers `true` on the closed set's default arm.
585    ///
586    /// # Semantics — VARIANT match on the Option-defaulted nested
587    /// scalar, not POPULATED Option
588    ///
589    /// `has_optimization_direction(kind)` returns `true` iff
590    /// `self.horizon.direction.unwrap_or_default() == kind`.
591    /// [`OptimizationDirection`] carries `#[default] Minimize` via
592    /// the derived [`Default`] impl, so a Process filled through
593    /// [`Horizon::bounded`] (which leaves `direction: None`) or
594    /// through `Horizon::default()` (same shape, `direction: None`)
595    /// answers `true` on [`OptimizationDirection::Minimize`] and
596    /// `false` on [`OptimizationDirection::Maximize`]. This mirrors
597    /// the default-arm short-circuit contract every other closed-set-
598    /// defaulted-child probe on [`Classification`] publishes
599    /// (`has_calm`, `has_data_classification`, `has_horizon_kind`) —
600    /// the `Option`-hop is soft-mapped to the closed set's default
601    /// arm rather than surfaced as a distinct presence axis. A
602    /// regression that flipped [`OptimizationDirection`]'s
603    /// `#[default]` off `Minimize` (which would silently invert every
604    /// unadorned `Asymptotic` Process's rate-window evaluator
605    /// polarity — see the [`OptimizationDirection::Minimize`] variant
606    /// docstring) fails at this probe's default-arm tests before
607    /// drifting through every downstream consumer.
608    ///
609    /// # Semantics rationale — Option-hop as default vs presence
610    ///
611    /// The `direction: Option<OptimizationDirection>` slot on
612    /// [`Horizon`] is documented as "Asymptotic only" — a `Bounded`
613    /// horizon has no meaningful direction so the operator leaves
614    /// it `None`. Yet the closed set carries `#[default] Minimize`,
615    /// so a bare `Bounded` Process's optimization direction reads
616    /// as `Minimize` at every consumer downstream via
617    /// [`Option::unwrap_or_default`]. That default IS the substrate's
618    /// operator-facing answer for "what direction would this Process
619    /// optimize toward if it became Asymptotic without further
620    /// annotation?", and a `:requires (optimization-direction-
621    /// Minimize)` audit at the checks.lisp surface correctly matches
622    /// every unadorned Process — matching the corner-property contract
623    /// every other defaulted-child probe publishes. An operator who
624    /// wants a strict presence axis (`is direction *actually* set?`)
625    /// gets that answer through a distinct future primitive
626    /// (`has_optimization_direction_set`) that would read the
627    /// `is_some` bit alone — orthogonal to this variant-equality
628    /// probe. This method commits to the variant-equality
629    /// interpretation so the corner-property contract stays uniform
630    /// with the four scalar-carrier peers.
631    ///
632    /// # Compounding
633    ///
634    /// This method POPULATES the (required-parent × nested-struct-
635    /// scalar-child) corner at its SECOND substrate primitive after
636    /// [`Self::has_horizon_kind`] opened it — pinning the corner as
637    /// a proven-repeatable primitive shape rather than a single-
638    /// example curiosity, and DEMONSTRATING that the corner admits
639    /// both direct-scalar and Option-scalar traversals through the
640    /// same nested-struct intermediary via the closed set's default.
641    /// A future co-tenant on this corner (a peer probe on another
642    /// nested-struct's scalar or Option-scalar discriminator, e.g.
643    /// a hypothetical `has_backend_port_family` reaching
644    /// `spec.routing.as_ref().and_then(|r| r.backend.tls_issuer.as_ref()).is_some()`
645    /// or a nested-struct-scalar discriminator on
646    /// `spec.encapsulates.<some-inner>.kind`) lands as ONE peer
647    /// inherent method with the same two-hop `self.<outer>.<inner>`
648    /// walk (with or without an Option-hop threading through the
649    /// closed set's `Default`) and routes through the same
650    /// `strip_and_classify_prefixed_kind::<K, _>` shape in
651    /// `tatara-check`. A future [`OptimizationDirection`] variant
652    /// (a hypothetical `Stabilize` sentinel for "drive toward a
653    /// target value", pre-flagged on the closed set's `ALL`
654    /// docstring) reaches every downstream through ONE `ALL` entry
655    /// + one `as_str` arm + one `prefers_lower` arm + one
656    /// `is_improvement` arm on the closed set with the probe body
657    /// untouched.
658    ///
659    /// Theory anchor: THEORY.md §II.1 invariant 5 — composition
660    /// preserves proofs; the nested-struct-Option-scalar-carrier
661    /// presence-probe body lives at ONE substrate site so every
662    /// downstream (`optimization-direction-<kind>` require-tag family
663    /// in `tatara-check`, future audit dispatchers walking
664    /// [`OptimizationDirection::ALL`], future variant additions on
665    /// [`OptimizationDirection`]) binds through the SAME
666    /// `has(kind)` shape rather than restating the
667    /// `classification.horizon.direction.unwrap_or_default() == kind`
668    /// closure body at each callsite. THEORY.md §VI.1 — generation
669    /// over composition; a future [`OptimizationDirection`] variant
670    /// lands at ONE `ALL` entry + ONE `as_str` arm on the closed set
671    /// and the probe picks it up mechanically without further
672    /// per-consumer edits.
673    #[must_use]
674    pub fn has_optimization_direction(&self, kind: OptimizationDirection) -> bool {
675        self.horizon.direction.unwrap_or_default() == kind
676    }
677}
678
679/// Structural type — how data flows through the point.
680///
681/// Closed-set sibling on the classification axis algebra; the `ALL` /
682/// `as_str` / Display / `FromStr` triad mirrors
683/// [`DataClassification::ALL`], [`crate::pool::PoolPhase::ALL`],
684/// [`crate::pool::MemberState::ALL`], [`crate::pool::ReplacementPolicy::ALL`],
685/// [`crate::pool::ReturnPolicy::ALL`],
686/// [`crate::boundary::ConditionKind::ALL`],
687/// [`crate::lifetime::TeardownPolicy::ALL`],
688/// [`crate::lifetime::LifetimeKind::ALL`],
689/// [`crate::intent::IntentKind::ALL`],
690/// [`crate::phase::ProcessPhase::ALL`],
691/// [`crate::signal::ProcessSignal::ALL`]. The
692/// `(input_arity, output_arity)` projection (via [`Arity`]) closes the
693/// graph-topology contract: each variant lands in exactly one of the
694/// three structural buckets — endomorphic (1→1), diffusive (1→N), or
695/// convergent (N→1) — so future DAG composition / edge-cardinality
696/// validators dispatch on a typed projection rather than re-deriving
697/// from variant names.
698#[derive(
699    Clone,
700    Copy,
701    Debug,
702    PartialEq,
703    Eq,
704    Hash,
705    Serialize,
706    Deserialize,
707    JsonSchema,
708    tatara_closed_set::DeriveClosedSet,
709)]
710#[serde(rename_all = "PascalCase")]
711#[closed_set(via = "as_str", generate_unknown, display)]
712pub enum ConvergencePointType {
713    /// 1 input → 1 output (linear conversion).
714    Transform,
715    /// 1 input → N outputs (fan-out, spawns downstream DAGs).
716    Fork,
717    /// N inputs → 1 output (fan-in, merges upstream results).
718    Join,
719    /// N inputs → 1 output (barrier, waits for all inputs).
720    Gate,
721    /// N inputs → 1 output (choice, picks best by policy).
722    Select,
723    /// 1 input → N outputs same type (replicate signal).
724    Broadcast,
725    /// N inputs → 1 output (fold/aggregate).
726    Reduce,
727    /// 1 input → 1 output + side-channel (tap for observation).
728    Observe,
729}
730
731impl ConvergencePointType {
732    /// The closed set of point types — single source of truth that
733    /// drives the `as_str` / Display / `FromStr` triad AND the
734    /// `(input_arity, output_arity)` typed pair (via [`Arity`]) AND the
735    /// `is_endomorphic` / `is_diffusive` / `is_convergent` predicate
736    /// triple. Adding a ninth variant lands at one `ALL` entry + one
737    /// `as_str` arm + one `input_arity` arm + one `output_arity` arm +
738    /// one arm per predicate — exhaustively checked by the compiler
739    /// (the `[Self; 8]` array literal forces the arity) AND by the
740    /// per-variant truth-table contract test (a new variant must
741    /// declare its own `(input, output)` arity pair or any future
742    /// DAG composition validator that dispatches on
743    /// `(input_arity, output_arity)` will silently mis-wire it).
744    /// Closes the load-bearing classification-axis enum that
745    /// `tatara_core::domain::compliance_binding::PointSelector::ByType`
746    /// already dispatches against and that every `Process`'s
747    /// `Classification.point_type` reads as the topological identity
748    /// of the convergence point.
749    pub const ALL: [Self; 8] = [
750        Self::Transform,
751        Self::Fork,
752        Self::Join,
753        Self::Gate,
754        Self::Select,
755        Self::Broadcast,
756        Self::Reduce,
757        Self::Observe,
758    ];
759
760    /// Canonical PascalCase wire-format projection — matches the
761    /// serde `rename_all = "PascalCase"` output verbatim AND the CRD
762    /// `enum:` enumeration that the Process schema stamps on
763    /// `spec.classification.pointType`. Pinned by
764    /// `convergence_point_type_as_str_matches_serde` so a variant
765    /// rename can't drift between the typed surface, the CRD enum,
766    /// the YAML wire format AND any future operator-facing
767    /// diagnostic that composes `pointType={kind}` via Display
768    /// rather than a hard-coded literal that would silently rot.
769    /// Display + FromStr triad over `ALL` mirrors `DataClassification`
770    /// / `PoolPhase` / `MemberState` / `ReplacementPolicy` /
771    /// `ReturnPolicy` / `TeardownPolicy` / `ConditionKind` /
772    /// `ProcessPhase` / `ProcessSignal`.
773    pub const fn as_str(self) -> &'static str {
774        match self {
775            Self::Transform => "Transform",
776            Self::Fork => "Fork",
777            Self::Join => "Join",
778            Self::Gate => "Gate",
779            Self::Select => "Select",
780            Self::Broadcast => "Broadcast",
781            Self::Reduce => "Reduce",
782            Self::Observe => "Observe",
783        }
784    }
785
786    /// Cardinality of the input edge into this point — `One` for
787    /// `Transform | Fork | Broadcast | Observe` (single-source
788    /// projections), `Many` for `Join | Gate | Select | Reduce`
789    /// (multi-source convergent reductions). Closed-set match (not
790    /// `matches!`) so a future variant triggers the compiler's
791    /// exhaustiveness check at this site rather than silently
792    /// defaulting to `One`. Paired with [`Self::output_arity`] they
793    /// form the typed `(input, output)` projection that future
794    /// DAG composition validators (edge-cardinality checks: "you
795    /// can't connect a Fork's output to a Transform's input
796    /// without a Join in between") dispatch against — a single
797    /// projection per variant means a future `Demux` / `Mux` /
798    /// `Pipeline` point lands in exactly one cell of the
799    /// `Arity × Arity` topology table rather than rotting against
800    /// open-coded `== ConvergencePointType::Fork` checks.
801    pub const fn input_arity(self) -> Arity {
802        match self {
803            Self::Transform | Self::Fork | Self::Broadcast | Self::Observe => Arity::One,
804            Self::Join | Self::Gate | Self::Select | Self::Reduce => Arity::Many,
805        }
806    }
807
808    /// Cardinality of the output edge from this point — `Many` for
809    /// `Fork | Broadcast` (fan-out), `One` for everything else.
810    /// Closed-set match so a future variant triggers the compiler's
811    /// exhaustiveness check. See [`Self::input_arity`] for the
812    /// arity-pair contract + bucket definitions.
813    pub const fn output_arity(self) -> Arity {
814        match self {
815            Self::Fork | Self::Broadcast => Arity::Many,
816            Self::Transform
817            | Self::Join
818            | Self::Gate
819            | Self::Select
820            | Self::Reduce
821            | Self::Observe => Arity::One,
822        }
823    }
824
825    /// Does this point preserve the single-input single-output
826    /// shape? `(input, output) == (One, One)` — `Transform`
827    /// (identity-shaped reshape) and `Observe` (passthrough +
828    /// side-channel tap). Closed-set match so a future variant
829    /// triggers the compiler's exhaustiveness check. Paired with
830    /// `is_diffusive` and `is_convergent` they form the three-way
831    /// disjoint bucket carving sealed by
832    /// `convergence_point_type_buckets_cover_every_variant` AND
833    /// `convergence_point_type_arity_pair_agrees_with_bucket` —
834    /// the bridge that lets the bucket predicates and the arity
835    /// pair name the same topology partition from two angles.
836    pub const fn is_endomorphic(self) -> bool {
837        match self {
838            Self::Transform | Self::Observe => true,
839            Self::Fork
840            | Self::Join
841            | Self::Gate
842            | Self::Select
843            | Self::Broadcast
844            | Self::Reduce => false,
845        }
846    }
847
848    /// Does this point fan out — single input replicated/split
849    /// across many outputs? `(input, output) == (One, Many)` —
850    /// `Fork` and `Broadcast`. Closed-set match so a future variant
851    /// triggers the compiler's exhaustiveness check. See
852    /// `is_endomorphic` for the bucket-carving contract.
853    pub const fn is_diffusive(self) -> bool {
854        match self {
855            Self::Fork | Self::Broadcast => true,
856            Self::Transform
857            | Self::Join
858            | Self::Gate
859            | Self::Select
860            | Self::Reduce
861            | Self::Observe => false,
862        }
863    }
864
865    /// Does this point reduce — many inputs collapsed to one
866    /// output? `(input, output) == (Many, One)` — `Join`, `Gate`,
867    /// `Select`, `Reduce`. Closed-set match so a future variant
868    /// triggers the compiler's exhaustiveness check. See
869    /// `is_endomorphic` for the bucket-carving contract. The
870    /// impossible `(Many, Many)` topology bucket is pinned empty
871    /// by `convergence_point_type_arity_pair_agrees_with_bucket`
872    /// — a `(Many, Many)` point would mean "many independent
873    /// inputs replicated across many independent outputs", which
874    /// has no convergence semantics: every DAG-composition
875    /// validator would have to special-case it. A future variant
876    /// that wants `(Many, Many)` must first extend the bucket
877    /// carving deliberately.
878    pub const fn is_convergent(self) -> bool {
879        match self {
880            Self::Join | Self::Gate | Self::Select | Self::Reduce => true,
881            Self::Transform | Self::Fork | Self::Broadcast | Self::Observe => false,
882        }
883    }
884}
885
886// `impl FromStr for ConvergencePointType` +
887// `impl tatara_lisp::ClosedSet for ConvergencePointType` +
888// `impl std::fmt::Display for ConvergencePointType` +
889// `pub struct UnknownConvergencePointType(pub String)` are all generated
890// by `#[derive(tatara_closed_set::DeriveClosedSet)]` +
891// `#[closed_set(via = "as_str", generate_unknown, display)]` on the
892// enum declaration above. `label` delegates to the inherent
893// `ConvergencePointType::as_str` — the inherent name (PascalCase
894// `as_str`) stays the load-bearing wire-vocabulary projection that
895// matches the serde `rename_all = "PascalCase"` output AND the CRD
896// `enum:` enumeration the Process schema stamps on
897// `spec.classification.pointType` verbatim, while generic
898// `T: ClosedSet` consumers reach the STABLE workspace-wide name
899// (`label`). The `display` flag emits the
900// `f.write_str(self.as_str())` delegation block at the same
901// proc-macro site rather than a hand-rolled `fmt::Display` block per
902// implementor. The auto-derived carrier label "convergence point
903// type" matches the prior hand-rolled `#[error("unknown convergence
904// point type: {0}")]` annotation byte-for-byte. Symmetric to the
905// other five classification-axis closed-sets in this file
906// (`SubstrateType` / `HorizonKind` / `OptimizationDirection` /
907// `CalmClassification` / `DataClassification`) AND every other
908// `#[derive(DeriveClosedSet)]` implementor across the workspace
909// (`crate::pool::{ReplacementPolicy,MemberState,PoolPhase,ReturnPolicy}`,
910// `crate::export::{ArtifactKind,ReportFormat,ChannelKind,ExportTrigger}`,
911// `crate::allocation::{RequestorKind,AllocationPhase}`).
912
913/// Edge cardinality of a [`ConvergencePointType`]'s input or output.
914///
915/// Typed projection used by [`ConvergencePointType::input_arity`] and
916/// [`ConvergencePointType::output_arity`] so DAG composition validators
917/// reach for a closed-set enum rather than re-deriving the in/out
918/// cardinality from variant names. `Many` is the "≥1, could be N"
919/// cardinality — it carries no upper bound because the convergence
920/// point's variant tag is already the structural identity; the
921/// number itself is a runtime property of the DAG, not the typescape.
922#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, tatara_closed_set::DeriveClosedSet)]
923#[closed_set(via = "as_str", display, generate_unknown)]
924pub enum Arity {
925    /// Single edge — exactly one input or one output.
926    One,
927    /// Multiple edges — any number ≥ 1.
928    Many,
929}
930
931impl Arity {
932    /// The closed set of arities — single source of truth that
933    /// drives `as_str` / Display AND the `is_one` predicate. Adding
934    /// a third variant (e.g. `Arity::Zero` for sinks) lands at one
935    /// `ALL` entry + one `as_str` arm + one predicate arm —
936    /// exhaustively checked by the compiler.
937    pub const ALL: [Self; 2] = [Self::One, Self::Many];
938
939    /// Canonical projection — `"One" | "Many"`. Pinned by
940    /// `arity_display_matches_as_str` so a future Display impl
941    /// can't drift from the canonical string.
942    pub const fn as_str(self) -> &'static str {
943        match self {
944            Self::One => "One",
945            Self::Many => "Many",
946        }
947    }
948
949    /// Is this the single-edge cardinality? Closed-set match (not
950    /// `matches!`) so a future variant triggers the compiler's
951    /// exhaustiveness check.
952    pub const fn is_one(self) -> bool {
953        match self {
954            Self::One => true,
955            Self::Many => false,
956        }
957    }
958}
959
960// `impl fmt::Display for Arity` + `impl std::str::FromStr for Arity` +
961// `impl tatara_lisp::ClosedSet for Arity` + `pub struct UnknownArity(pub
962// String)` are all generated by
963// `#[derive(tatara_closed_set::DeriveClosedSet)]` +
964// `#[closed_set(via = "as_str", display, generate_unknown)]` on the enum
965// declaration above. The inherent `as_str` projection stays load-bearing
966// — the canonical `"One" | "Many"` string every DAG composition
967// validator reads; `via = "as_str"` binds `ClosedSet::label` to the same
968// projection so the substrate-wide `assert_display_matches_label` /
969// `assert_closed_set_well_formed` primitives dispatch through the same
970// byte-identical shape every other closed-set implementor across the
971// crate publishes. Aligns `Arity` with the substrate-wide
972// `#[derive(DeriveClosedSet)]` idiom that every other closed-set enum on
973// this classification axis (`ConvergencePointType`, `SubstrateType`,
974// `HorizonKind`, `OptimizationDirection`, `CalmClassification`,
975// `DataClassification`) already carries — the last hand-rolled
976// `impl fmt::Display` on the axis is closed at ONE substrate site.
977
978/// Operational substrate.
979///
980/// Closed-set sibling on the classification axis algebra; the `ALL` /
981/// `as_str` / Display / `FromStr` triad mirrors
982/// [`ConvergencePointType::ALL`], [`DataClassification::ALL`],
983/// [`crate::pool::PoolPhase::ALL`], [`crate::pool::MemberState::ALL`],
984/// [`crate::pool::ReplacementPolicy::ALL`],
985/// [`crate::pool::ReturnPolicy::ALL`],
986/// [`crate::boundary::ConditionKind::ALL`],
987/// [`crate::lifetime::TeardownPolicy::ALL`],
988/// [`crate::lifetime::LifetimeKind::ALL`],
989/// [`crate::intent::IntentKind::ALL`],
990/// [`crate::phase::ProcessPhase::ALL`],
991/// [`crate::signal::ProcessSignal::ALL`]. The
992/// `is_resource` / `is_policy` / `is_telemetry` predicate triple
993/// carves the eight variants into three structurally-disjoint
994/// substrate planes — resource (you allocate from it), policy (it
995/// gates access for other workloads), telemetry (it observes other
996/// workloads) — so future compliance-baseline selectors that
997/// dispatch on a substrate's plane (resource budgets only apply to
998/// resource substrates; policy substrates inherit baselines from
999/// what they govern; telemetry substrates inherit baselines from
1000/// what they observe) read a typed projection rather than
1001/// re-deriving from variant names.
1002#[derive(
1003    Clone,
1004    Copy,
1005    Debug,
1006    PartialEq,
1007    Eq,
1008    Hash,
1009    PartialOrd,
1010    Ord,
1011    Serialize,
1012    Deserialize,
1013    JsonSchema,
1014    tatara_closed_set::DeriveClosedSet,
1015)]
1016#[serde(rename_all = "PascalCase")]
1017#[closed_set(via = "as_str", generate_unknown, display)]
1018pub enum SubstrateType {
1019    Financial,
1020    Compute,
1021    Network,
1022    Storage,
1023    Security,
1024    Identity,
1025    Observability,
1026    Regulatory,
1027}
1028
1029impl SubstrateType {
1030    /// The closed set of substrates — single source of truth that
1031    /// drives the `as_str` / Display / `FromStr` triad AND the
1032    /// `is_resource` / `is_policy` / `is_telemetry` predicate triple.
1033    /// Adding a ninth variant lands at one `ALL` entry + one
1034    /// `as_str` arm + one arm per predicate — exhaustively checked
1035    /// by the compiler (the `[Self; 8]` array literal forces the
1036    /// arity) AND by the per-variant plane-bucket contract test (a
1037    /// new variant must declare its own plane or any future
1038    /// compliance-baseline selector that dispatches on
1039    /// `(is_resource, is_policy, is_telemetry)` will silently
1040    /// mis-classify it). Closes the load-bearing classification-axis
1041    /// enum that
1042    /// `tatara_core::domain::compliance_binding::PointSelector::BySubstrate`
1043    /// already dispatches against and that every `Process`'s
1044    /// `Classification.substrate` reads as the operational
1045    /// substrate the convergence point lives on.
1046    pub const ALL: [Self; 8] = [
1047        Self::Financial,
1048        Self::Compute,
1049        Self::Network,
1050        Self::Storage,
1051        Self::Security,
1052        Self::Identity,
1053        Self::Observability,
1054        Self::Regulatory,
1055    ];
1056
1057    /// Canonical PascalCase wire-format projection — matches the
1058    /// serde `rename_all = "PascalCase"` output verbatim AND the CRD
1059    /// `enum:` enumeration that the Process schema stamps on
1060    /// `spec.classification.substrate`. Pinned by
1061    /// `substrate_type_as_str_matches_serde` so a variant rename
1062    /// can't drift between the typed surface, the CRD enum, the YAML
1063    /// wire format AND any future operator-facing diagnostic that
1064    /// composes `substrate={kind}` via Display rather than a
1065    /// hard-coded literal that would silently rot. Display + FromStr
1066    /// triad over `ALL` mirrors `ConvergencePointType` /
1067    /// `DataClassification` / `PoolPhase` / `MemberState` /
1068    /// `ReplacementPolicy` / `ReturnPolicy` / `TeardownPolicy` /
1069    /// `ConditionKind` / `ProcessPhase` / `ProcessSignal`.
1070    pub const fn as_str(self) -> &'static str {
1071        match self {
1072            Self::Financial => "Financial",
1073            Self::Compute => "Compute",
1074            Self::Network => "Network",
1075            Self::Storage => "Storage",
1076            Self::Security => "Security",
1077            Self::Identity => "Identity",
1078            Self::Observability => "Observability",
1079            Self::Regulatory => "Regulatory",
1080        }
1081    }
1082
1083    /// Is this a resource substrate — one you allocate budgets from
1084    /// to run workloads? `Financial | Compute | Network | Storage`.
1085    /// Closed-set match (not `matches!`) so a future variant
1086    /// triggers the compiler's exhaustiveness check at this site
1087    /// rather than silently defaulting to `false`. Paired with
1088    /// `is_policy` and `is_telemetry` they form the three-way
1089    /// disjoint plane carving sealed by
1090    /// `substrate_type_buckets_cover_every_variant` — the bridge
1091    /// that lets future compliance-baseline selectors dispatch on
1092    /// plane without re-deriving from variant names.
1093    pub const fn is_resource(self) -> bool {
1094        match self {
1095            Self::Financial | Self::Compute | Self::Network | Self::Storage => true,
1096            Self::Security | Self::Identity | Self::Observability | Self::Regulatory => false,
1097        }
1098    }
1099
1100    /// Is this a policy substrate — one that gates access or
1101    /// compliance for other workloads rather than carrying their
1102    /// payload? `Security | Identity | Regulatory`. Closed-set match
1103    /// so a future variant triggers the compiler's exhaustiveness
1104    /// check. See `is_resource` for the bucket-carving contract.
1105    pub const fn is_policy(self) -> bool {
1106        match self {
1107            Self::Security | Self::Identity | Self::Regulatory => true,
1108            Self::Financial
1109            | Self::Compute
1110            | Self::Network
1111            | Self::Storage
1112            | Self::Observability => false,
1113        }
1114    }
1115
1116    /// Is this a telemetry substrate — one that passively observes
1117    /// other workloads (metrics, logs, traces) without carrying
1118    /// their payload or gating their access? `Observability` only.
1119    /// Closed-set match so a future variant triggers the compiler's
1120    /// exhaustiveness check. See `is_resource` for the
1121    /// bucket-carving contract. A telemetry substrate's compliance
1122    /// baseline is inherited from what it observes — the singleton
1123    /// bucket is intentional, not a placeholder.
1124    pub const fn is_telemetry(self) -> bool {
1125        match self {
1126            Self::Observability => true,
1127            Self::Financial
1128            | Self::Compute
1129            | Self::Network
1130            | Self::Storage
1131            | Self::Security
1132            | Self::Identity
1133            | Self::Regulatory => false,
1134        }
1135    }
1136}
1137
1138// `impl FromStr for SubstrateType` +
1139// `impl tatara_lisp::ClosedSet for SubstrateType` +
1140// `impl std::fmt::Display for SubstrateType` +
1141// `pub struct UnknownSubstrateType(pub String)` are all generated by
1142// `#[derive(tatara_closed_set::DeriveClosedSet)]` +
1143// `#[closed_set(via = "as_str", generate_unknown, display)]` on the
1144// enum declaration above. The auto-derived carrier label "substrate
1145// type" matches the prior hand-rolled `#[error("unknown substrate
1146// type: {0}")]` annotation byte-for-byte. See the retrofit comment
1147// block on [`ConvergencePointType`] for the canonical narrative.
1148
1149/// How long the point runs. Flattened struct-of-optionals so the OpenAPI
1150/// schema carries a single `kind` discriminator without per-variant merge.
1151#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
1152#[serde(rename_all = "camelCase")]
1153pub struct Horizon {
1154    #[serde(default)]
1155    pub kind: HorizonKind,
1156    /// Metric being optimized (Asymptotic only).
1157    #[serde(default, skip_serializing_if = "Option::is_none")]
1158    pub metric: Option<String>,
1159    /// Whether to minimize or maximize the metric (Asymptotic only).
1160    #[serde(default, skip_serializing_if = "Option::is_none")]
1161    pub direction: Option<OptimizationDirection>,
1162    /// Rate threshold considered healthy (Asymptotic only).
1163    #[serde(default, skip_serializing_if = "Option::is_none")]
1164    pub healthy_rate_threshold: Option<f64>,
1165}
1166
1167/// The shape of a convergence horizon's lifetime — does the point
1168/// run toward a fixed point and terminate, or run in perpetuity with
1169/// a rate signal?
1170///
1171/// Closed-set sibling on the classification axis algebra; the `ALL` /
1172/// `as_str` / Display / `FromStr` triad mirrors
1173/// [`ConvergencePointType::ALL`], [`SubstrateType::ALL`],
1174/// [`DataClassification::ALL`], [`CalmClassification::ALL`],
1175/// [`OptimizationDirection::ALL`], [`crate::pool::PoolPhase::ALL`],
1176/// [`crate::pool::MemberState::ALL`],
1177/// [`crate::pool::ReplacementPolicy::ALL`],
1178/// [`crate::pool::ReturnPolicy::ALL`],
1179/// [`crate::boundary::ConditionKind::ALL`],
1180/// [`crate::lifetime::TeardownPolicy::ALL`],
1181/// [`crate::lifetime::LifetimeKind::ALL`],
1182/// [`crate::intent::IntentKind::ALL`],
1183/// [`crate::phase::ProcessPhase::ALL`],
1184/// [`crate::signal::ProcessSignal::ALL`]. The [`Self::terminates`]
1185/// predicate is the load-bearing horizon-shape primitive — schedulers
1186/// asking "will this Process ever reach `Reaped` via natural
1187/// termination?" read it as the typed image of the lattice ordering
1188/// (`Bounded ≤ Asymptotic` because the bounded horizon strictly
1189/// refines the asymptotic one by also terminating) rather than
1190/// re-deriving from the variant name. The
1191/// [`Self::requires_metric_axes`] predicate is the typed validity
1192/// witness for the [`Horizon`] struct's three `Option<…>` fields
1193/// (`metric`, `direction`, `healthy_rate_threshold`) — they're
1194/// `Some(_)` iff the kind requires them, so the implicit invariant
1195/// the optionality encodes becomes a checkable per-kind predicate
1196/// instead of operator folklore.
1197#[derive(
1198    Clone,
1199    Copy,
1200    Debug,
1201    PartialEq,
1202    Eq,
1203    Hash,
1204    Serialize,
1205    Deserialize,
1206    JsonSchema,
1207    Default,
1208    tatara_closed_set::DeriveClosedSet,
1209)]
1210#[serde(rename_all = "PascalCase")]
1211#[closed_set(via = "as_str", generate_unknown, display)]
1212pub enum HorizonKind {
1213    /// Has a fixed point — distance reaches 0 and terminates.
1214    #[default]
1215    Bounded,
1216    /// Runs in perpetuity — rate is the health signal, not distance.
1217    Asymptotic,
1218}
1219
1220impl HorizonKind {
1221    /// The closed set of horizon kinds — single source of truth that
1222    /// drives the `as_str` / Display / `FromStr` triad AND the
1223    /// `terminates` predicate AND the `requires_metric_axes` shape-
1224    /// validity witness. Adding a third variant (e.g. a `Periodic`
1225    /// sentinel for "terminates on each window boundary then
1226    /// re-arms", which neither perpetually-running nor singularly-
1227    /// terminating names) lands at one `ALL` entry + one `as_str`
1228    /// arm + one `terminates` arm + one `requires_metric_axes` arm —
1229    /// exhaustively checked by the compiler (the `[Self; 2]` array
1230    /// literal forces the arity) AND by the per-variant truth-table
1231    /// tests (a new variant must declare its own termination AND
1232    /// metric-axes requirement, or every scheduler / horizon-shape
1233    /// validator will silently bucket it). Closes the load-bearing
1234    /// classification sub-axis that the `Horizon.kind` field threads
1235    /// through every `Classification.horizon` field on every
1236    /// Process — the last open sibling on the classification axis
1237    /// algebra after `OptimizationDirection` (980a318),
1238    /// `CalmClassification` (da3430c), `SubstrateType` (b9d7b3b),
1239    /// `ConvergencePointType` (7941527), `Arity`, and
1240    /// `DataClassification` (81bffa0).
1241    pub const ALL: [Self; 2] = [Self::Bounded, Self::Asymptotic];
1242
1243    /// Canonical PascalCase wire-format projection — matches the
1244    /// serde `rename_all = "PascalCase"` output verbatim AND the CRD
1245    /// `enum:` enumeration the Process schema stamps on
1246    /// `spec.classification.horizon.kind`. Pinned by
1247    /// `horizon_kind_as_str_matches_serde` so a variant rename
1248    /// can't drift between the typed surface, the CRD enum, the
1249    /// YAML wire format AND any future operator-facing diagnostic
1250    /// composing `horizon.kind={kind}` via Display rather than a
1251    /// hard-coded literal. Display + FromStr triad over `ALL`
1252    /// mirrors every sibling closed-set enum in this crate.
1253    pub const fn as_str(self) -> &'static str {
1254        match self {
1255            Self::Bounded => "Bounded",
1256            Self::Asymptotic => "Asymptotic",
1257        }
1258    }
1259
1260    /// LOAD-BEARING HORIZON-SHAPE PRIMITIVE: does this kind terminate
1261    /// naturally — i.e. does it have a fixed point that
1262    /// `ConvergenceDistance` can reach? Closed-set match (not
1263    /// `matches!`) so a future variant triggers the compiler's
1264    /// exhaustiveness check rather than silently defaulting to
1265    /// `false` (which would silently mis-route a terminating
1266    /// variant through the asymptotic rate-window evaluator) or
1267    /// `true` (which would silently invent a fixed point for a
1268    /// perpetual variant). `Bounded ⇒ true`, `Asymptotic ⇒ false`
1269    /// is the typed image of the documented lattice ordering
1270    /// `Bounded ≤ Asymptotic` — the bounded horizon strictly refines
1271    /// the asymptotic one BY ALSO TERMINATING. Future schedulers
1272    /// asking "will this Process reach `Reaped` via natural
1273    /// termination?" read this predicate, and the tatara-lattice
1274    /// `Lattice for Horizon` impl (which currently dispatches on
1275    /// `self.kind == HorizonKind::Bounded` at three sites) can be
1276    /// recast in a future run to read `self.kind.terminates()` so
1277    /// the lattice basis is the typed primitive rather than a
1278    /// variant-name comparison.
1279    pub const fn terminates(self) -> bool {
1280        match self {
1281            Self::Bounded => true,
1282            Self::Asymptotic => false,
1283        }
1284    }
1285
1286    /// LOAD-BEARING SHAPE-VALIDITY WITNESS: does this kind require
1287    /// the three asymptotic-only [`Horizon`] axes (`metric`,
1288    /// `direction`, `healthy_rate_threshold`) to be `Some(_)`?
1289    /// Closed-set match (not `matches!`) so a future variant
1290    /// triggers the compiler's exhaustiveness check rather than
1291    /// silently defaulting to `false` (which would silently let an
1292    /// asymptotic-shaped variant ship with missing metric axes and
1293    /// trip the rate-window evaluator at runtime). `Bounded ⇒
1294    /// false`, `Asymptotic ⇒ true` is the typed image of the
1295    /// optionality the [`Horizon`] struct encodes via three
1296    /// `Option<…>` fields — the implicit invariant ("Asymptotic
1297    /// only" in the field docs) is now a checkable per-kind
1298    /// predicate. Future horizon-shape validators (CRD admission,
1299    /// `tatara-check` form linter, Lisp authoring-time predicate)
1300    /// read this rather than re-deriving from variant names.
1301    /// Pinned as the antisymmetric partner of [`Self::terminates`]
1302    /// — exactly one of `(terminates, requires_metric_axes)` is
1303    /// true per variant — by
1304    /// `horizon_kind_terminate_xor_requires_metric_axes`.
1305    pub const fn requires_metric_axes(self) -> bool {
1306        match self {
1307            Self::Bounded => false,
1308            Self::Asymptotic => true,
1309        }
1310    }
1311}
1312
1313// `impl FromStr for HorizonKind` +
1314// `impl tatara_lisp::ClosedSet for HorizonKind` +
1315// `impl std::fmt::Display for HorizonKind` +
1316// `pub struct UnknownHorizonKind(pub String)` are all generated by
1317// `#[derive(tatara_closed_set::DeriveClosedSet)]` +
1318// `#[closed_set(via = "as_str", generate_unknown, display)]` on the
1319// enum declaration above. The auto-derived carrier label "horizon
1320// kind" matches the prior hand-rolled `#[error("unknown horizon
1321// kind: {0}")]` annotation byte-for-byte. See the retrofit comment
1322// block on [`ConvergencePointType`] for the canonical narrative.
1323
1324impl Horizon {
1325    pub fn bounded() -> Self {
1326        Self::default()
1327    }
1328
1329    pub fn asymptotic(
1330        metric: impl Into<String>,
1331        direction: OptimizationDirection,
1332        threshold: f64,
1333    ) -> Self {
1334        Self {
1335            kind: HorizonKind::Asymptotic,
1336            metric: Some(metric.into()),
1337            direction: Some(direction),
1338            healthy_rate_threshold: Some(threshold),
1339        }
1340    }
1341}
1342
1343/// Direction of asymptotic optimization — does the metric trend
1344/// downward (cost / latency / error rate) or upward
1345/// (throughput / coverage / revenue)?
1346///
1347/// Closed-set sibling on the classification axis algebra; the `ALL` /
1348/// `as_str` / Display / `FromStr` triad mirrors
1349/// [`ConvergencePointType::ALL`], [`SubstrateType::ALL`],
1350/// [`DataClassification::ALL`], [`CalmClassification::ALL`],
1351/// [`crate::pool::PoolPhase::ALL`], [`crate::pool::MemberState::ALL`],
1352/// [`crate::pool::ReplacementPolicy::ALL`],
1353/// [`crate::pool::ReturnPolicy::ALL`],
1354/// [`crate::boundary::ConditionKind::ALL`],
1355/// [`crate::lifetime::TeardownPolicy::ALL`],
1356/// [`crate::lifetime::LifetimeKind::ALL`],
1357/// [`crate::intent::IntentKind::ALL`],
1358/// [`crate::phase::ProcessPhase::ALL`],
1359/// [`crate::signal::ProcessSignal::ALL`]. The
1360/// [`Self::is_improvement`] predicate is the load-bearing
1361/// optimization primitive — `Asymptotic` horizons read it as the
1362/// typed image of "did this metric sample improve over the last
1363/// one?" rather than re-deriving `<` vs `>` from the variant name
1364/// at every consumer site (rate-window evaluators, breathe-band
1365/// regression detectors, asymptotic-health probes).
1366#[derive(
1367    Clone,
1368    Copy,
1369    Debug,
1370    PartialEq,
1371    Eq,
1372    Hash,
1373    Serialize,
1374    Deserialize,
1375    JsonSchema,
1376    Default,
1377    tatara_closed_set::DeriveClosedSet,
1378)]
1379#[serde(rename_all = "PascalCase")]
1380#[closed_set(via = "as_str", generate_unknown, display)]
1381pub enum OptimizationDirection {
1382    /// Cost / latency / error rate — lower is better. The default for
1383    /// an under-specified `Asymptotic` horizon so an unannotated
1384    /// metric can't silently flip the rate-window evaluator's polarity
1385    /// (a future `Maximize`-default-via-rename would silently invert
1386    /// every existing alert that treats decreasing rate as healthy).
1387    #[default]
1388    Minimize,
1389    /// Throughput / coverage / revenue — higher is better.
1390    Maximize,
1391}
1392
1393impl OptimizationDirection {
1394    /// The closed set of optimization directions — single source of
1395    /// truth that drives the `as_str` / Display / `FromStr` triad AND
1396    /// the `prefers_lower` partition AND the `is_improvement`
1397    /// load-bearing primitive AND both `From` bridge arms. Adding a
1398    /// third variant (e.g. a `Stabilize` sentinel for "drive toward
1399    /// a target value", which neither minimization nor maximization
1400    /// names) lands at one `ALL` entry + one `as_str` arm + one
1401    /// `prefers_lower` arm + one `is_improvement` arm + two bridge
1402    /// arms — exhaustively checked by the compiler (the `[Self; 2]`
1403    /// array literal forces the arity) AND by the per-variant
1404    /// truth-table tests (a new variant must declare its own
1405    /// improvement semantics, or every asymptotic-health probe will
1406    /// silently bucket it). Closes the load-bearing classification
1407    /// sub-axis that the `Horizon.direction` field threads through
1408    /// every `Asymptotic` Process.
1409    pub const ALL: [Self; 2] = [Self::Minimize, Self::Maximize];
1410
1411    /// Canonical PascalCase wire-format projection — matches the serde
1412    /// `rename_all = "PascalCase"` output verbatim AND the CRD `enum:`
1413    /// enumeration the Process schema stamps on
1414    /// `spec.classification.horizon.direction`. Pinned by
1415    /// `optimization_direction_as_str_matches_serde` so a variant
1416    /// rename can't drift between the typed surface, the CRD enum, the
1417    /// YAML wire format AND any future operator-facing diagnostic
1418    /// composed as `direction={kind}` via Display rather than a
1419    /// hard-coded literal. Display + `FromStr` triad over `ALL`
1420    /// mirrors every sibling closed-set enum in this crate.
1421    pub const fn as_str(self) -> &'static str {
1422        match self {
1423            Self::Minimize => "Minimize",
1424            Self::Maximize => "Maximize",
1425        }
1426    }
1427
1428    /// Does this direction prefer numerically lower values?
1429    /// Closed-set match (not `matches!`) so a future variant triggers
1430    /// the compiler's exhaustiveness check at this site rather than
1431    /// silently defaulting to `false` (which would mis-bucket a
1432    /// `Stabilize`-style variant onto the maximization path). The
1433    /// boolean partition is the algebraic shape of an optimization
1434    /// direction: `Minimize ⇒ true`, `Maximize ⇒ false`. Mirrors
1435    /// [`CalmClassification::requires_coordination`] — a two-variant
1436    /// truth-table that any future dispatch on a per-direction policy
1437    /// (rate-window evaluator polarity, breathe-band regression
1438    /// detector sign, asymptotic-health threshold direction) reads
1439    /// once rather than re-deriving from the variant name.
1440    pub const fn prefers_lower(self) -> bool {
1441        match self {
1442            Self::Minimize => true,
1443            Self::Maximize => false,
1444        }
1445    }
1446
1447    /// LOAD-BEARING OPTIMIZATION PRIMITIVE: under this direction, is
1448    /// `after` strictly better than `before`? Closed-set match so a
1449    /// future variant triggers the compiler's exhaustiveness check
1450    /// rather than silently defaulting to `false` (which would
1451    /// silently mark every sample as a regression). For `Minimize`,
1452    /// improvement means `after < before`; for `Maximize`, `after >
1453    /// before`. Strict inequality so a no-op sample (equal values) is
1454    /// NOT counted as improvement — pinned by
1455    /// `optimization_direction_no_op_is_not_improvement`, which
1456    /// guarantees a flatlined rate-window evaluator doesn't silently
1457    /// keep claiming "still improving" forever and skipping the
1458    /// healthy-rate-threshold gate. NaN on either operand short-
1459    /// circuits to `false` (no improvement claim from indeterminate
1460    /// data) via the standard `PartialOrd` behavior — pinned by
1461    /// `optimization_direction_nan_is_not_improvement`. The
1462    /// asymmetry contract (`is_improvement(a, b)` xor
1463    /// `is_improvement(b, a)` for distinct finite samples) is pinned
1464    /// by `optimization_direction_is_improvement_is_antisymmetric`,
1465    /// the algebraic shape that every asymptotic-health rate-window
1466    /// evaluator depends on to avoid double-counting an improvement
1467    /// as a regression on the reverse traversal.
1468    pub fn is_improvement(self, before: f64, after: f64) -> bool {
1469        match self {
1470            Self::Minimize => after < before,
1471            Self::Maximize => after > before,
1472        }
1473    }
1474}
1475
1476// `impl FromStr for OptimizationDirection` +
1477// `impl tatara_lisp::ClosedSet for OptimizationDirection` +
1478// `impl std::fmt::Display for OptimizationDirection` +
1479// `pub struct UnknownOptimizationDirection(pub String)` are all
1480// generated by `#[derive(tatara_closed_set::DeriveClosedSet)]` +
1481// `#[closed_set(via = "as_str", generate_unknown, display)]` on the
1482// enum declaration above. The auto-derived carrier label
1483// "optimization direction" matches the prior hand-rolled
1484// `#[error("unknown optimization direction: {0}")]` annotation
1485// byte-for-byte. See the retrofit comment block on
1486// [`ConvergencePointType`] for the canonical narrative.
1487
1488/// CALM theorem classification — determines whether coordination is required.
1489///
1490/// Closed-set sibling on the classification axis algebra; the `ALL` /
1491/// `as_str` / Display / `FromStr` triad mirrors
1492/// [`ConvergencePointType::ALL`], [`SubstrateType::ALL`],
1493/// [`DataClassification::ALL`], [`crate::pool::PoolPhase::ALL`],
1494/// [`crate::pool::MemberState::ALL`], [`crate::pool::ReplacementPolicy::ALL`],
1495/// [`crate::pool::ReturnPolicy::ALL`],
1496/// [`crate::boundary::ConditionKind::ALL`],
1497/// [`crate::lifetime::TeardownPolicy::ALL`],
1498/// [`crate::lifetime::LifetimeKind::ALL`],
1499/// [`crate::intent::IntentKind::ALL`],
1500/// [`crate::phase::ProcessPhase::ALL`],
1501/// [`crate::signal::ProcessSignal::ALL`]. The
1502/// [`Self::requires_coordination`] predicate is the CALM theorem
1503/// keystone — Hellerstein's "Consistency As Logical Monotonicity"
1504/// states that a program can be distributed without coordination iff
1505/// it computes a monotone function, so `Monotone ⇒ no coordination`
1506/// and `NonMonotone ⇒ requires coordination` is a typed image of the
1507/// theorem itself rather than a runtime convention. Future reconciler
1508/// dispatch on `calm.requires_coordination()` (Raft for non-monotone
1509/// writes; gossip for monotone ones) reads this projection rather
1510/// than re-deriving from variant names.
1511#[derive(
1512    Clone,
1513    Copy,
1514    Debug,
1515    PartialEq,
1516    Eq,
1517    Hash,
1518    Serialize,
1519    Deserialize,
1520    JsonSchema,
1521    Default,
1522    tatara_closed_set::DeriveClosedSet,
1523)]
1524#[serde(rename_all = "PascalCase")]
1525#[closed_set(via = "as_str", generate_unknown, display)]
1526pub enum CalmClassification {
1527    /// Can be distributed without coordination (CALM ⇒ the program
1528    /// computes a monotone function).
1529    #[default]
1530    Monotone,
1531    /// Requires coordination (CALM ⇒ the program is not monotone).
1532    NonMonotone,
1533}
1534
1535impl CalmClassification {
1536    /// The closed set of CALM classifications — single source of truth
1537    /// that drives the `as_str` / Display / `FromStr` triad AND the
1538    /// `requires_coordination` predicate. Adding a third variant
1539    /// (e.g. a `ConditionallyMonotone` sentinel for ops that are
1540    /// monotone under a witness, like CRDT joins under a fixed
1541    /// schema) lands at one `ALL` entry + one `as_str` arm + one
1542    /// predicate arm + one bridge-pair arm — exhaustively checked by
1543    /// the compiler (the `[Self; 2]` array literal forces the arity)
1544    /// AND by the per-variant predicate truth-table test (a new
1545    /// variant must declare its own coordination requirement or any
1546    /// future reconciler-side dispatch will silently bucket it).
1547    /// Closes the load-bearing classification-axis enum that the
1548    /// `Classification.calm` field exposes to every Process and that
1549    /// [`tatara_lattice`]'s boolean-lattice `Lattice for
1550    /// CalmClassification` impl reads via [`Self::requires_coordination`]
1551    /// as the lattice's `top()` predicate.
1552    pub const ALL: [Self; 2] = [Self::Monotone, Self::NonMonotone];
1553
1554    /// Canonical PascalCase wire-format projection — matches the
1555    /// serde `rename_all = "PascalCase"` output verbatim AND the CRD
1556    /// `enum:` enumeration that the Process schema stamps on
1557    /// `spec.classification.calm`. Pinned by
1558    /// `calm_classification_as_str_matches_serde` so a variant rename
1559    /// can't drift between the typed surface, the CRD enum, the YAML
1560    /// wire format AND any future operator-facing diagnostic that
1561    /// composes `calm={kind}` via Display rather than a hard-coded
1562    /// literal that would silently rot. Display + FromStr triad over
1563    /// `ALL` mirrors every sibling closed-set enum in this crate.
1564    pub const fn as_str(self) -> &'static str {
1565        match self {
1566            Self::Monotone => "Monotone",
1567            Self::NonMonotone => "NonMonotone",
1568        }
1569    }
1570
1571    /// CALM-THEOREM KEYSTONE: does this classification require
1572    /// distributed coordination? Closed-set match (not `matches!`) so
1573    /// a future variant triggers the compiler's exhaustiveness check
1574    /// at this site rather than silently defaulting to `false` and
1575    /// shipping a non-monotone operation onto the no-coordination
1576    /// path. The theorem (Hellerstein 2010) states that a program can
1577    /// be distributed without coordination iff it computes a monotone
1578    /// function — `Monotone ⇒ false` and `NonMonotone ⇒ true` is the
1579    /// typed image of that biconditional. Consumers (future reconciler
1580    /// dispatch between Raft writes and gossip propagation; current
1581    /// `tatara_lattice` boolean-lattice ordering where `Monotone ≤
1582    /// NonMonotone`) read this predicate rather than re-deriving from
1583    /// variant names.
1584    pub const fn requires_coordination(self) -> bool {
1585        match self {
1586            Self::Monotone => false,
1587            Self::NonMonotone => true,
1588        }
1589    }
1590}
1591
1592// `impl FromStr for CalmClassification` +
1593// `impl tatara_lisp::ClosedSet for CalmClassification` +
1594// `impl std::fmt::Display for CalmClassification` +
1595// `pub struct UnknownCalmClassification(pub String)` are all generated
1596// by `#[derive(tatara_closed_set::DeriveClosedSet)]` +
1597// `#[closed_set(via = "as_str", generate_unknown, display)]` on the
1598// enum declaration above. The auto-derived carrier label
1599// "calm classification" matches the prior hand-rolled
1600// `#[error("unknown calm classification: {0}")]` annotation
1601// byte-for-byte. See the retrofit comment block on
1602// [`ConvergencePointType`] for the canonical narrative.
1603
1604/// Data sensitivity, drives compliance baseline selection.
1605///
1606/// Sibling closed-set on the classification axis algebra; the `ALL` /
1607/// `as_str` / Display / `FromStr` triad mirrors
1608/// [`crate::pool::PoolPhase::ALL`], [`crate::pool::MemberState::ALL`],
1609/// [`crate::pool::ReplacementPolicy::ALL`],
1610/// [`crate::pool::ReturnPolicy::ALL`],
1611/// [`crate::boundary::ConditionKind::ALL`],
1612/// [`crate::lifetime::TeardownPolicy::ALL`],
1613/// [`crate::lifetime::LifetimeKind::ALL`],
1614/// [`crate::intent::IntentKind::ALL`],
1615/// [`crate::phase::ProcessPhase::ALL`],
1616/// [`crate::signal::ProcessSignal::ALL`].
1617#[derive(
1618    Clone,
1619    Copy,
1620    Debug,
1621    PartialEq,
1622    Eq,
1623    PartialOrd,
1624    Ord,
1625    Hash,
1626    Serialize,
1627    Deserialize,
1628    JsonSchema,
1629    Default,
1630    tatara_closed_set::DeriveClosedSet,
1631)]
1632#[serde(rename_all = "PascalCase")]
1633#[closed_set(via = "as_str", generate_unknown, display)]
1634pub enum DataClassification {
1635    Public,
1636    #[default]
1637    Internal,
1638    Confidential,
1639    Pii,
1640    Phi,
1641    Pci,
1642}
1643
1644impl DataClassification {
1645    /// The closed set of data classifications — single source of truth
1646    /// that drives the `as_str` / Display / `FromStr` triad AND the
1647    /// `sensitivity_rank` total-order projection AND the
1648    /// `is_restricted` / `is_regulated` predicate pair. Adding a
1649    /// seventh variant lands at one `ALL` entry + one `as_str` arm +
1650    /// one `sensitivity_rank` arm + one arm per predicate —
1651    /// exhaustively checked by the compiler (the `[Self; 6]` array
1652    /// literal forces the arity) AND by the per-variant truth-table
1653    /// contract test (a new variant must declare its own
1654    /// `(is_restricted, is_regulated)` bucket or any future
1655    /// compliance-baseline auto-selector that dispatches on the pair
1656    /// will silently bucket it into the wrong sensitivity column).
1657    /// This closes the sixth classification-axis enum and the closure
1658    /// is consumed by [`tatara_lattice`]'s total-order `Lattice` impl
1659    /// via [`Self::sensitivity_rank`] so the lattice ordering no
1660    /// longer rides silently on declaration order.
1661    pub const ALL: [Self; 6] = [
1662        Self::Public,
1663        Self::Internal,
1664        Self::Confidential,
1665        Self::Pii,
1666        Self::Phi,
1667        Self::Pci,
1668    ];
1669
1670    /// Canonical PascalCase wire-format projection — matches the
1671    /// serde `rename_all = "PascalCase"` output verbatim AND the CRD
1672    /// `enum:` enumeration that the Process schema stamps on
1673    /// `spec.classification.dataClassification`. Pinned by
1674    /// `data_classification_as_str_matches_serde` so a variant rename
1675    /// can't drift between the typed surface, the CRD enum, the YAML
1676    /// wire format AND any future operator-facing diagnostic that
1677    /// composes `dataClassification={class}` via Display rather than
1678    /// a hard-coded literal that would silently rot. Display +
1679    /// FromStr triad over `ALL` mirrors `PoolPhase` / `MemberState` /
1680    /// `ReplacementPolicy` / `ReturnPolicy` / `TeardownPolicy` /
1681    /// `ConditionKind` / `ProcessPhase` / `ProcessSignal`.
1682    pub const fn as_str(self) -> &'static str {
1683        match self {
1684            Self::Public => "Public",
1685            Self::Internal => "Internal",
1686            Self::Confidential => "Confidential",
1687            Self::Pii => "Pii",
1688            Self::Phi => "Phi",
1689            Self::Pci => "Pci",
1690        }
1691    }
1692
1693    /// Explicit total-order rank, sealed at one site so the lattice
1694    /// ordering stops riding silently on declaration order. Pre-lift
1695    /// the tatara-lattice `Lattice for DataClassification` impl
1696    /// compared variants via `(*self as u8) <= (*other as u8)`, so a
1697    /// future variant inserted in the middle of the enum (say a
1698    /// `Restricted` between `Internal` and `Confidential`) would
1699    /// silently shift every subsequent variant's `as u8` value AND
1700    /// the lattice's `leq` relation — no compile error, no test
1701    /// failure, but every compliance-baseline comparison
1702    /// downstream would have moved by one slot. Post-lift the rank
1703    /// is declared explicitly per variant; an insertion forces the
1704    /// author to pick a rank deliberately (and
1705    /// `data_classification_rank_is_strictly_monotone_over_all`
1706    /// pins the existing six variants at 0..6 so the lattice's
1707    /// total order remains the documented
1708    /// `Public < Internal < Confidential < Pii < Phi < Pci`).
1709    pub const fn sensitivity_rank(self) -> u8 {
1710        match self {
1711            Self::Public => 0,
1712            Self::Internal => 1,
1713            Self::Confidential => 2,
1714            Self::Pii => 3,
1715            Self::Phi => 4,
1716            Self::Pci => 5,
1717        }
1718    }
1719
1720    /// Is this classification subject to external regulatory regime
1721    /// (HIPAA / PCI-DSS / GDPR-style data-subject controls)?
1722    /// Closed-set match (not `matches!`) so a future variant triggers
1723    /// the compiler's exhaustiveness check at this site rather than
1724    /// silently defaulting to `false`. Paired with `is_restricted`
1725    /// they form the two-axis projection that future
1726    /// compliance-baseline auto-selectors dispatch against —
1727    /// `(false, false)` ⇒ freely distributable (`Public`);
1728    /// `(false, true)` ⇒ access-controlled but not regulated
1729    /// (`Internal | Confidential`); `(true, true)` ⇒ regulated data
1730    /// that implies access control (`Pii | Phi | Pci`). The
1731    /// impossible bucket `(true, false)` — regulated data without
1732    /// access control — is pinned empty by
1733    /// `data_classification_regulated_implies_restricted`.
1734    pub const fn is_regulated(self) -> bool {
1735        match self {
1736            Self::Pii | Self::Phi | Self::Pci => true,
1737            Self::Public | Self::Internal | Self::Confidential => false,
1738        }
1739    }
1740
1741    /// Does this classification require access controls beyond
1742    /// freely-distributable? Closed-set match so a future variant
1743    /// triggers the compiler's exhaustiveness check. See
1744    /// `is_regulated` for the predicate-pair contract + bucket
1745    /// definitions.
1746    pub const fn is_restricted(self) -> bool {
1747        match self {
1748            Self::Public => false,
1749            Self::Internal | Self::Confidential | Self::Pii | Self::Phi | Self::Pci => true,
1750        }
1751    }
1752}
1753
1754// `impl FromStr for DataClassification` +
1755// `impl tatara_lisp::ClosedSet for DataClassification` +
1756// `impl std::fmt::Display for DataClassification` +
1757// `pub struct UnknownDataClassification(pub String)` are all generated
1758// by `#[derive(tatara_closed_set::DeriveClosedSet)]` +
1759// `#[closed_set(via = "as_str", generate_unknown, display)]` on the
1760// enum declaration above. The auto-derived carrier label
1761// "data classification" matches the prior hand-rolled
1762// `#[error("unknown data classification: {0}")]` annotation
1763// byte-for-byte. See the retrofit comment block on
1764// [`ConvergencePointType`] for the canonical narrative.
1765
1766// ───────────────────────────── bridges to tatara-core ─────────────────
1767
1768impl From<ConvergencePointType> for core::ConvergencePointType {
1769    fn from(v: ConvergencePointType) -> Self {
1770        use ConvergencePointType::*;
1771        match v {
1772            Transform => Self::Transform,
1773            Fork => Self::Fork,
1774            Join => Self::Join,
1775            Gate => Self::Gate,
1776            Select => Self::Select,
1777            Broadcast => Self::Broadcast,
1778            Reduce => Self::Reduce,
1779            Observe => Self::Observe,
1780        }
1781    }
1782}
1783
1784impl From<core::ConvergencePointType> for ConvergencePointType {
1785    fn from(v: core::ConvergencePointType) -> Self {
1786        use core::ConvergencePointType as C;
1787        match v {
1788            C::Transform => Self::Transform,
1789            C::Fork => Self::Fork,
1790            C::Join => Self::Join,
1791            C::Gate => Self::Gate,
1792            C::Select => Self::Select,
1793            C::Broadcast => Self::Broadcast,
1794            C::Reduce => Self::Reduce,
1795            C::Observe => Self::Observe,
1796        }
1797    }
1798}
1799
1800impl From<SubstrateType> for core::SubstrateType {
1801    fn from(v: SubstrateType) -> Self {
1802        use SubstrateType::*;
1803        match v {
1804            Financial => Self::Financial,
1805            Compute => Self::Compute,
1806            Network => Self::Network,
1807            Storage => Self::Storage,
1808            Security => Self::Security,
1809            Identity => Self::Identity,
1810            Observability => Self::Observability,
1811            Regulatory => Self::Regulatory,
1812        }
1813    }
1814}
1815
1816impl From<core::SubstrateType> for SubstrateType {
1817    fn from(v: core::SubstrateType) -> Self {
1818        use core::SubstrateType as C;
1819        match v {
1820            C::Financial => Self::Financial,
1821            C::Compute => Self::Compute,
1822            C::Network => Self::Network,
1823            C::Storage => Self::Storage,
1824            C::Security => Self::Security,
1825            C::Identity => Self::Identity,
1826            C::Observability => Self::Observability,
1827            C::Regulatory => Self::Regulatory,
1828        }
1829    }
1830}
1831
1832impl From<OptimizationDirection> for core::OptimizationDirection {
1833    fn from(v: OptimizationDirection) -> Self {
1834        match v {
1835            OptimizationDirection::Minimize => Self::Minimize,
1836            OptimizationDirection::Maximize => Self::Maximize,
1837        }
1838    }
1839}
1840
1841impl From<core::OptimizationDirection> for OptimizationDirection {
1842    fn from(v: core::OptimizationDirection) -> Self {
1843        use core::OptimizationDirection as C;
1844        match v {
1845            C::Minimize => Self::Minimize,
1846            C::Maximize => Self::Maximize,
1847        }
1848    }
1849}
1850
1851impl From<Horizon> for core::ConvergenceHorizon {
1852    fn from(v: Horizon) -> Self {
1853        match v.kind {
1854            HorizonKind::Bounded => Self::Bounded,
1855            HorizonKind::Asymptotic => Self::Asymptotic {
1856                metric: v.metric.unwrap_or_default(),
1857                direction: v.direction.unwrap_or_default().into(),
1858                healthy_rate_threshold: v.healthy_rate_threshold.unwrap_or_default(),
1859            },
1860        }
1861    }
1862}
1863
1864impl From<CalmClassification> for core::CalmClassification {
1865    fn from(v: CalmClassification) -> Self {
1866        match v {
1867            CalmClassification::Monotone => Self::Monotone,
1868            CalmClassification::NonMonotone => Self::NonMonotone,
1869        }
1870    }
1871}
1872
1873impl From<core::CalmClassification> for CalmClassification {
1874    fn from(v: core::CalmClassification) -> Self {
1875        use core::CalmClassification as C;
1876        match v {
1877            C::Monotone => Self::Monotone,
1878            C::NonMonotone => Self::NonMonotone,
1879        }
1880    }
1881}
1882
1883impl From<DataClassification> for core_compl::DataClassification {
1884    fn from(v: DataClassification) -> Self {
1885        use DataClassification::*;
1886        match v {
1887            Public => Self::Public,
1888            Internal => Self::Internal,
1889            Confidential => Self::Confidential,
1890            Pii => Self::Pii,
1891            Phi => Self::Phi,
1892            Pci => Self::Pci,
1893        }
1894    }
1895}
1896
1897impl From<core_compl::DataClassification> for DataClassification {
1898    fn from(v: core_compl::DataClassification) -> Self {
1899        use core_compl::DataClassification as C;
1900        match v {
1901            C::Public => Self::Public,
1902            C::Internal => Self::Internal,
1903            C::Confidential => Self::Confidential,
1904            C::Pii => Self::Pii,
1905            C::Phi => Self::Phi,
1906            C::Pci => Self::Pci,
1907        }
1908    }
1909}
1910
1911#[cfg(test)]
1912mod tests {
1913    use super::*;
1914    // The closed-set tests below call `T::from_str(bad)` via the
1915    // derive-generated `FromStr` impls — bring the trait into scope at
1916    // the test module so the lib body doesn't carry an otherwise-unused
1917    // `use std::str::FromStr;` at the file head.
1918    use std::str::FromStr;
1919
1920    #[test]
1921    fn bridges_roundtrip() {
1922        let pt: core::ConvergencePointType = ConvergencePointType::Gate.into();
1923        let back: ConvergencePointType = pt.into();
1924        assert_eq!(back, ConvergencePointType::Gate);
1925
1926        let sub: core::SubstrateType = SubstrateType::Observability.into();
1927        let back: SubstrateType = sub.into();
1928        assert_eq!(back, SubstrateType::Observability);
1929    }
1930
1931    #[test]
1932    fn data_classification_ordering() {
1933        assert!(DataClassification::Public < DataClassification::Pii);
1934        assert!(DataClassification::Internal < DataClassification::Confidential);
1935    }
1936
1937    #[test]
1938    fn horizon_default_is_bounded() {
1939        assert_eq!(Horizon::default().kind, HorizonKind::Bounded);
1940    }
1941
1942    // ── Classification::gate_compute substrate pins ─────────────────────
1943    //
1944    // The six-line `Classification { point_type: Gate, substrate: Compute,
1945    // horizon: Default::default(), calm: Default::default(),
1946    // data_classification: Default::default() }` struct-literal was
1947    // open-coded verbatim at ten hand-authored callsites before the
1948    // primitive closed it. These pins bind the composed shape at
1949    // fail-before-pass-after granularity so a regression that flipped a
1950    // baseline axis, drifted a sibling default, or leaked a non-baseline
1951    // slot into the substrate composer surfaces HERE rather than as
1952    // silent operator-visible drift at every unadorned ephemeral env
1953    // (the one production consumer, `default_ephemeral_class`) AND every
1954    // downstream test fixture that keys assertions on the shape.
1955
1956    #[test]
1957    fn gate_compute_composes_the_five_baseline_axes() {
1958        // Primary shape: every axis parked at the workspace baseline.
1959        // A regression that flipped `point_type` off `Gate` or
1960        // `substrate` off `Compute` — the two axes with no `Default` —
1961        // surfaces here.
1962        let c = Classification::gate_compute();
1963        assert_eq!(c.point_type, ConvergencePointType::Gate);
1964        assert_eq!(c.substrate, SubstrateType::Compute);
1965        assert_eq!(c.horizon, Horizon::default());
1966        assert_eq!(c.calm, CalmClassification::default());
1967        assert_eq!(c.data_classification, DataClassification::default());
1968    }
1969
1970    #[test]
1971    fn gate_compute_defaulted_axes_ride_sibling_closed_set_defaults() {
1972        // Pins the sibling-default correspondence the doc comment
1973        // names — a regression that flipped a sibling default (a new
1974        // `HorizonKind` variant promoted to `#[default]`, a rename of
1975        // `CalmClassification::Monotone`, a promotion of `Pii` above
1976        // `Internal` in the `DataClassification` ordering) would move
1977        // the baseline HERE rather than at every downstream consumer.
1978        let c = Classification::gate_compute();
1979        assert_eq!(c.horizon.kind, HorizonKind::Bounded);
1980        assert_eq!(c.calm, CalmClassification::Monotone);
1981        assert_eq!(c.data_classification, DataClassification::Internal);
1982    }
1983
1984    #[test]
1985    fn gate_compute_matches_hand_authored_pre_lift_bytewise() {
1986        // Byte-identical parity with the pre-lift six-line struct-literal
1987        // that recurred at ten hand-authored sites. A regression that
1988        // reshaped the primitive would diverge from the pre-lift block
1989        // HERE rather than at every downstream fixture that keys on the
1990        // shape.
1991        let composed = Classification::gate_compute();
1992        let hand_authored = Classification {
1993            point_type: ConvergencePointType::Gate,
1994            substrate: SubstrateType::Compute,
1995            horizon: Horizon::default(),
1996            calm: CalmClassification::default(),
1997            data_classification: DataClassification::default(),
1998        };
1999        assert_eq!(composed, hand_authored);
2000    }
2001
2002    #[test]
2003    fn gate_compute_is_call_time_construction_not_a_shared_singleton() {
2004        // Two independent calls produce structurally-equal but distinct
2005        // values — pins that the primitive is a plain constructor
2006        // rather than a `lazy_static` clone (which would leak a shared
2007        // singleton whose in-place mutation at one consumer would
2008        // silently mutate the shape at every other consumer). The `!=`
2009        // check on `&mut _`-obtained pointer addresses is intentional:
2010        // a shared singleton would collide, and the pin catches the
2011        // regression at the primitive rather than at the operator-facing
2012        // shape-drift downstream.
2013        let a = Classification::gate_compute();
2014        let b = Classification::gate_compute();
2015        assert_eq!(a, b);
2016        assert!(!std::ptr::eq(&a, &b));
2017    }
2018
2019    // ── closed-set algebra contracts for DataClassification
2020    //    (ALL × as_str × FromStr × rank × predicate pair) ────────────
2021
2022    /// Structural well-formedness of [`DataClassification`] as a
2023    /// [`tatara_lisp::ClosedSet`] implementor — the workspace-wide
2024    /// testkit lift that pins all three structural invariants (`ALL`
2025    /// is non-empty, every variant round-trips through
2026    /// `label ↔ parse_label`, labels are pairwise distinct, `""` is
2027    /// outside the closed set) at ONE call site. Replaces the hand-
2028    /// derived `data_classification_all_is_unique_and_complete` +
2029    /// `data_classification_roundtrip_via_as_str` + the empty-input arm
2030    /// of `unknown_data_classification_errors`. `FromStr` delegates to
2031    /// `<Self as tatara_closed_set::ClosedSet>::parse_label`, so this helper
2032    /// exercises the same code path the reconciler hits when parsing a
2033    /// CRD `enum:`-validated `dataClassification` value back to the
2034    /// typed classification.
2035    #[test]
2036    fn data_classification_is_well_formed_closed_set() {
2037        tatara_closed_set::assert_closed_set_well_formed::<DataClassification>();
2038    }
2039
2040    /// CANONICAL-KEY CONTRACT: `as_str` matches serde's PascalCase
2041    /// output verbatim for every variant. A future variant rename (or
2042    /// an `as_str` arm typo) lands here at one site, instead of
2043    /// drifting between the typed surface, the CRD enum, and the YAML
2044    /// wire format the reconciler stamps on
2045    /// `spec.classification.dataClassification`.
2046    #[test]
2047    fn data_classification_as_str_matches_serde() {
2048        crate::tagged_union::assert_label_matches_serde_serialization::<DataClassification>();
2049    }
2050
2051    /// The Display impl IS `as_str` — pinning this lets future callers
2052    /// reach for either projection without drift. Any operator-facing
2053    /// "dataClassification={class}" diagnostic that composes through
2054    /// Display inherits the canonical wire-format string automatically.
2055    #[test]
2056    fn data_classification_display_matches_as_str() {
2057        crate::tagged_union::assert_display_matches_label::<DataClassification>();
2058    }
2059
2060    /// `FromStr` rejects strings that aren't in the canonical
2061    /// projection — lowercased / typo / cross-axis-leaked — and the
2062    /// error echoes the input verbatim so the operator-facing
2063    /// diagnostic carries the offending value, not a normalized form.
2064    /// The empty-input arm is pinned by
2065    /// [`data_classification_is_well_formed_closed_set`] via the
2066    /// `tatara_lisp::ClosedSet` testkit; the cases here pin the
2067    /// verbatim-echo contract on the [`UnknownDataClassification`]
2068    /// newtype, which the trait's `make_unknown` can't see.
2069    #[test]
2070    fn unknown_data_classification_errors() {
2071        for bad in [
2072            "pii",          // lowercased
2073            "PII",          // uppercased
2074            "PersonalData", // typo
2075            "internal_data",
2076            "Steady",   // PoolPhase-axis leak
2077            "Replace",  // ReturnPolicy-axis leak
2078            "Attested", // ProcessPhase-axis leak
2079            "Compute",  // SubstrateType-axis leak
2080            "Gate",     // ConvergencePointType-axis leak
2081            "Monotone", // CalmClassification-axis leak
2082        ] {
2083            let err = DataClassification::from_str(bad).unwrap_err();
2084            assert_eq!(err.0, bad, "error payload should echo input verbatim");
2085        }
2086    }
2087
2088    // `unknown_data_classification_message_matches_substrate_convention`
2089    // removed — clause (5) of
2090    // `tatara_closed_set::assert_closed_set_well_formed::<DataClassification>()`
2091    // verifies the substrate-wide `"unknown {SET_LABEL}: {input}"`
2092    // shape generically (called from
2093    // `data_classification_is_well_formed_closed_set` above); the
2094    // `SET_LABEL` projection is pinned by
2095    // `tatara_lisp_derive::pascal_to_spaced_lowercase_tests`.
2096
2097    /// TRUTH-TABLE CONTRACT: the predicate pair agrees with the
2098    /// documented per-variant compliance role. Pinning this table at
2099    /// one site means any future compliance-baseline auto-selector
2100    /// reads the same projection that the reconciler writes.
2101    #[test]
2102    fn data_classification_predicate_truth_tables() {
2103        assert!(!DataClassification::Public.is_restricted());
2104        assert!(!DataClassification::Public.is_regulated());
2105
2106        assert!(DataClassification::Internal.is_restricted());
2107        assert!(!DataClassification::Internal.is_regulated());
2108
2109        assert!(DataClassification::Confidential.is_restricted());
2110        assert!(!DataClassification::Confidential.is_regulated());
2111
2112        assert!(DataClassification::Pii.is_restricted());
2113        assert!(DataClassification::Pii.is_regulated());
2114
2115        assert!(DataClassification::Phi.is_restricted());
2116        assert!(DataClassification::Phi.is_regulated());
2117
2118        assert!(DataClassification::Pci.is_restricted());
2119        assert!(DataClassification::Pci.is_regulated());
2120    }
2121
2122    /// IMPLICATION CONTRACT: every regulated classification is also
2123    /// restricted. The impossible bucket (regulated AND
2124    /// freely-distributable) is pinned empty so a future variant that
2125    /// returned `(true, false)` from the predicate pair would FAIL
2126    /// here, forcing the author to either flip `is_restricted` or
2127    /// extend the consumer dispatch sites (compliance-baseline
2128    /// auto-selector, audit-log mandatory-fields validator)
2129    /// deliberately rather than silently producing a regulated class
2130    /// the API server would accept as freely-distributable. Encoded as
2131    /// material implication `is_regulated → is_restricted` so the
2132    /// boolean reads as the documented contract, not its NAND form.
2133    #[test]
2134    fn data_classification_regulated_implies_restricted() {
2135        for class in DataClassification::ALL {
2136            assert!(
2137                !class.is_regulated() || class.is_restricted(),
2138                "{class:?} is regulated but not restricted — \
2139                 regulated data is by definition not freely distributable",
2140            );
2141        }
2142    }
2143
2144    /// COVERAGE CONTRACT: every variant lands in exactly one of three
2145    /// compliance buckets — freely distributable (`Public`),
2146    /// restricted-only (`Internal | Confidential`), or regulated
2147    /// (`Pii | Phi | Pci`). Pins the three buckets at their declared
2148    /// cardinalities (1, 2, 3 — sum to `ALL.len()`) so a future
2149    /// variant lands somewhere deliberately.
2150    #[test]
2151    fn data_classification_buckets_cover_every_variant() {
2152        let mut free = 0u32;
2153        let mut restricted_only = 0u32;
2154        let mut regulated = 0u32;
2155        for class in DataClassification::ALL {
2156            match (class.is_restricted(), class.is_regulated()) {
2157                (false, false) => free += 1,
2158                (true, false) => restricted_only += 1,
2159                (true, true) => regulated += 1,
2160                (false, true) => {
2161                    panic!("regulated_implies_restricted already pins this empty for {class:?}")
2162                }
2163            }
2164        }
2165        assert_eq!(free, 1, "free bucket: Public");
2166        assert_eq!(
2167            restricted_only, 2,
2168            "restricted-only bucket: Internal + Confidential"
2169        );
2170        assert_eq!(regulated, 3, "regulated bucket: Pii + Phi + Pci");
2171        assert_eq!(
2172            free + restricted_only + regulated,
2173            DataClassification::ALL.len() as u32
2174        );
2175    }
2176
2177    /// MONOTONE-RANK CONTRACT: `sensitivity_rank` is strictly
2178    /// monotone over `ALL`'s declared order, so the lattice ordering
2179    /// `Public < Internal < Confidential < Pii < Phi < Pci` is sealed
2180    /// at one site (this enum's projection) instead of riding on the
2181    /// silent `as u8` cast in [`tatara_lattice`]. A future variant
2182    /// inserted in the middle would either preserve strict monotonicity
2183    /// here (and the lattice keeps working) or FAIL here at compile or
2184    /// test time (and the author has to renumber deliberately). Also
2185    /// pins the rank codomain at `0..ALL.len()` so no variant can
2186    /// silently outrank the documented top.
2187    #[test]
2188    fn data_classification_rank_is_strictly_monotone_over_all() {
2189        let ranks: Vec<u8> = DataClassification::ALL
2190            .into_iter()
2191            .map(DataClassification::sensitivity_rank)
2192            .collect();
2193        for win in ranks.windows(2) {
2194            assert!(win[0] < win[1], "ranks not strictly monotone: {ranks:?}");
2195        }
2196        assert_eq!(*ranks.first().unwrap(), 0, "bottom rank must be 0");
2197        assert_eq!(
2198            *ranks.last().unwrap(),
2199            (DataClassification::ALL.len() as u8) - 1,
2200            "top rank must be ALL.len() - 1"
2201        );
2202    }
2203
2204    /// RANK-AGREES-WITH-ORD CONTRACT: the typed `sensitivity_rank`
2205    /// projection agrees with the derived `PartialOrd` / `Ord` for
2206    /// every pair in `ALL × ALL`. This is the bridge that lets
2207    /// [`tatara_lattice`]'s total-order `Lattice for DataClassification`
2208    /// impl call `sensitivity_rank` instead of `as u8` without changing
2209    /// any observable lattice behavior — and it lets a future
2210    /// reordering of the enum's variant declarations land at this test
2211    /// site (forcing the rank arms to be renumbered) rather than
2212    /// silently shifting the lattice's `leq` relation.
2213    #[test]
2214    fn data_classification_rank_agrees_with_partial_ord() {
2215        for a in DataClassification::ALL {
2216            for b in DataClassification::ALL {
2217                assert_eq!(
2218                    a.sensitivity_rank() <= b.sensitivity_rank(),
2219                    a <= b,
2220                    "rank vs. PartialOrd drift on ({a:?}, {b:?})"
2221                );
2222            }
2223        }
2224    }
2225
2226    /// DEFAULT-AGREEMENT CONTRACT: `DataClassification::default()`
2227    /// returns `Internal` (the variant tagged `#[default]`), AND that
2228    /// variant lands in the restricted-only bucket — neither freely
2229    /// distributable nor externally regulated. A future `#[default]`
2230    /// rename without flipping the predicates fails here.
2231    #[test]
2232    fn data_classification_default_is_internal_in_restricted_only_bucket() {
2233        let d = DataClassification::default();
2234        assert_eq!(d, DataClassification::Internal);
2235        assert!(d.is_restricted());
2236        assert!(!d.is_regulated());
2237        assert_eq!(d.sensitivity_rank(), 1);
2238    }
2239
2240    /// BRIDGE ROUND-TRIP CONTRACT: every variant survives the
2241    /// CRD-facing (`PascalCase`) ↔ tatara-core (`snake_case`)
2242    /// `From` hop. Today the bridge is two hand-written 6-arm matches
2243    /// in this file; pinning the round-trip over `ALL` means a future
2244    /// variant added without extending the bridge fails here at one
2245    /// site instead of drifting between the CRD wire format and the
2246    /// `core_compl::DataClassification` selector axis.
2247    #[test]
2248    fn data_classification_bridge_roundtrip_over_all() {
2249        for class in DataClassification::ALL {
2250            let core: core_compl::DataClassification = class.into();
2251            let back: DataClassification = core.into();
2252            assert_eq!(back, class, "bridge round-trip failed for {class:?}");
2253        }
2254    }
2255
2256    // ── closed-set algebra contracts for ConvergencePointType
2257    //    (ALL × as_str × FromStr × arity-pair × predicate triple) ────
2258
2259    /// Structural well-formedness of [`ConvergencePointType`] as a
2260    /// [`tatara_lisp::ClosedSet`] implementor — the workspace-wide
2261    /// testkit lift that pins all three structural invariants (`ALL`
2262    /// is non-empty, every variant round-trips through `label ↔
2263    /// parse_label`, labels are pairwise distinct, `""` is outside
2264    /// the closed set) at ONE call site. Replaces the hand-derived
2265    /// `convergence_point_type_all_is_unique_and_complete` +
2266    /// `convergence_point_type_roundtrip_via_as_str` + the empty-
2267    /// input arm of `unknown_convergence_point_type_errors`.
2268    /// `FromStr` delegates to `<Self as tatara_closed_set::ClosedSet>::parse_label`,
2269    /// so this helper exercises the same code path the reconciler
2270    /// hits when parsing a CRD `enum:`-validated value back to the
2271    /// typed point-type. The forced `[Self; 8]` array literal on
2272    /// `ConvergencePointType::ALL` still pins the cardinality at the
2273    /// declaration site.
2274    #[test]
2275    fn convergence_point_type_is_well_formed_closed_set() {
2276        tatara_closed_set::assert_closed_set_well_formed::<ConvergencePointType>();
2277    }
2278
2279    /// CANONICAL-KEY CONTRACT: `as_str` matches serde's PascalCase
2280    /// output verbatim for every variant. A future variant rename (or
2281    /// an `as_str` arm typo) lands here at one site, instead of
2282    /// drifting between the typed surface, the CRD enum, and the YAML
2283    /// wire format the reconciler reads from
2284    /// `spec.classification.pointType`.
2285    #[test]
2286    fn convergence_point_type_as_str_matches_serde() {
2287        crate::tagged_union::assert_label_matches_serde_serialization::<ConvergencePointType>();
2288    }
2289
2290    /// The Display impl IS `as_str` — pinning this lets future callers
2291    /// reach for either projection without drift.
2292    #[test]
2293    fn convergence_point_type_display_matches_as_str() {
2294        crate::tagged_union::assert_display_matches_label::<ConvergencePointType>();
2295    }
2296
2297    /// `FromStr` rejects strings outside the canonical projection —
2298    /// lowercased / typo / cross-axis-leaked — and the error echoes
2299    /// the input verbatim so the operator-facing diagnostic surfaces
2300    /// the bad value, not a normalized form. The empty-input arm is
2301    /// pinned by [`convergence_point_type_is_well_formed_closed_set`]
2302    /// via the `tatara_lisp::ClosedSet` testkit; the cases here pin
2303    /// the verbatim-echo contract on the
2304    /// [`UnknownConvergencePointType`] newtype, which the trait's
2305    /// `make_unknown` can't see.
2306    #[test]
2307    fn unknown_convergence_point_type_errors() {
2308        for bad in [
2309            "gate",       // lowercased
2310            "GATE",       // uppercased
2311            "Transformr", // typo
2312            "Filter",
2313            "Steady",   // PoolPhase-axis leak
2314            "Pii",      // DataClassification-axis leak
2315            "Attested", // ProcessPhase-axis leak
2316            "Compute",  // SubstrateType-axis leak
2317            "Monotone", // CalmClassification-axis leak
2318            "PromQL",   // ConditionKind-axis leak
2319        ] {
2320            let err = ConvergencePointType::from_str(bad).unwrap_err();
2321            assert_eq!(err.0, bad, "error payload should echo input verbatim");
2322        }
2323    }
2324
2325    // `unknown_convergence_point_type_message_matches_substrate_convention`
2326    // removed — clause (5) of
2327    // `tatara_closed_set::assert_closed_set_well_formed::<ConvergencePointType>()`
2328    // verifies the substrate-wide `"unknown {SET_LABEL}: {input}"`
2329    // shape generically (called from
2330    // `convergence_point_type_is_well_formed_closed_set` above); the
2331    // `SET_LABEL` projection is pinned by
2332    // `tatara_lisp_derive::pascal_to_spaced_lowercase_tests`.
2333
2334    /// TRUTH-TABLE CONTRACT: the predicate triple agrees with the
2335    /// documented per-variant topology role. Pinning this table at
2336    /// one site means any future DAG validator reads the same
2337    /// projection that compliance bindings dispatch against.
2338    #[test]
2339    fn convergence_point_type_predicate_truth_tables() {
2340        // Endomorphic: 1→1
2341        assert!(ConvergencePointType::Transform.is_endomorphic());
2342        assert!(!ConvergencePointType::Transform.is_diffusive());
2343        assert!(!ConvergencePointType::Transform.is_convergent());
2344
2345        assert!(ConvergencePointType::Observe.is_endomorphic());
2346        assert!(!ConvergencePointType::Observe.is_diffusive());
2347        assert!(!ConvergencePointType::Observe.is_convergent());
2348
2349        // Diffusive: 1→N
2350        assert!(!ConvergencePointType::Fork.is_endomorphic());
2351        assert!(ConvergencePointType::Fork.is_diffusive());
2352        assert!(!ConvergencePointType::Fork.is_convergent());
2353
2354        assert!(!ConvergencePointType::Broadcast.is_endomorphic());
2355        assert!(ConvergencePointType::Broadcast.is_diffusive());
2356        assert!(!ConvergencePointType::Broadcast.is_convergent());
2357
2358        // Convergent: N→1
2359        for t in [
2360            ConvergencePointType::Join,
2361            ConvergencePointType::Gate,
2362            ConvergencePointType::Select,
2363            ConvergencePointType::Reduce,
2364        ] {
2365            assert!(!t.is_endomorphic(), "{t:?} should not be endomorphic");
2366            assert!(!t.is_diffusive(), "{t:?} should not be diffusive");
2367            assert!(t.is_convergent(), "{t:?} should be convergent");
2368        }
2369    }
2370
2371    /// COVERAGE CONTRACT: every variant lands in *exactly one* of the
2372    /// three topology buckets — endomorphic, diffusive, or convergent.
2373    /// Pins the three buckets at their declared cardinalities (2, 2, 4
2374    /// — sum to `ALL.len()`) so a future variant lands somewhere
2375    /// deliberately. No variant returns true from more than one
2376    /// predicate; no variant returns false from all three.
2377    #[test]
2378    fn convergence_point_type_buckets_cover_every_variant() {
2379        let mut endomorphic = 0u32;
2380        let mut diffusive = 0u32;
2381        let mut convergent = 0u32;
2382        for t in ConvergencePointType::ALL {
2383            let buckets = [t.is_endomorphic(), t.is_diffusive(), t.is_convergent()];
2384            let hits: u32 = buckets.iter().map(|b| u32::from(*b)).sum();
2385            assert_eq!(
2386                hits, 1,
2387                "{t:?} landed in {hits} buckets: {buckets:?} (must be exactly one)"
2388            );
2389            if t.is_endomorphic() {
2390                endomorphic += 1;
2391            }
2392            if t.is_diffusive() {
2393                diffusive += 1;
2394            }
2395            if t.is_convergent() {
2396                convergent += 1;
2397            }
2398        }
2399        assert_eq!(endomorphic, 2, "endomorphic bucket: Transform + Observe");
2400        assert_eq!(diffusive, 2, "diffusive bucket: Fork + Broadcast");
2401        assert_eq!(
2402            convergent, 4,
2403            "convergent bucket: Join + Gate + Select + Reduce"
2404        );
2405        assert_eq!(
2406            endomorphic + diffusive + convergent,
2407            ConvergencePointType::ALL.len() as u32
2408        );
2409    }
2410
2411    /// ARITY-PAIR ⇔ BUCKET CONTRACT: the `(input_arity, output_arity)`
2412    /// projection names the same topology partition as the
2413    /// `is_endomorphic` / `is_diffusive` / `is_convergent` predicate
2414    /// triple. `(One, One) ⇒ endomorphic`; `(One, Many) ⇒ diffusive`;
2415    /// `(Many, One) ⇒ convergent`. The impossible `(Many, Many)`
2416    /// bucket is pinned empty here — a `(Many, Many)` point would
2417    /// have no convergence semantics (many independent inputs
2418    /// replicated across many independent outputs) and every future
2419    /// DAG-composition validator would have to special-case it. This
2420    /// seal is the bridge that lets a future graph validator dispatch
2421    /// on either projection (arity pair OR bucket predicates) without
2422    /// drift — and a future variant that wants `(Many, Many)` must
2423    /// extend the bucket carving deliberately rather than silently
2424    /// shipping a fourth topology class.
2425    #[test]
2426    fn convergence_point_type_arity_pair_agrees_with_bucket() {
2427        for t in ConvergencePointType::ALL {
2428            match (t.input_arity(), t.output_arity()) {
2429                (Arity::One, Arity::One) => assert!(
2430                    t.is_endomorphic(),
2431                    "{t:?} has (One, One) arity but is not endomorphic"
2432                ),
2433                (Arity::One, Arity::Many) => assert!(
2434                    t.is_diffusive(),
2435                    "{t:?} has (One, Many) arity but is not diffusive"
2436                ),
2437                (Arity::Many, Arity::One) => assert!(
2438                    t.is_convergent(),
2439                    "{t:?} has (Many, One) arity but is not convergent"
2440                ),
2441                (Arity::Many, Arity::Many) => panic!(
2442                    "{t:?} has (Many, Many) arity — pinned empty; \
2443                     extend the topology carving before adding a variant here"
2444                ),
2445            }
2446        }
2447    }
2448
2449    /// BRIDGE ROUND-TRIP CONTRACT: every variant survives the
2450    /// CRD-facing (`PascalCase`) ↔ tatara-core (`snake_case`)
2451    /// `From` hop. Today the bridge is two hand-written 8-arm
2452    /// matches in this file; pinning the round-trip over `ALL`
2453    /// means a future variant added without extending the bridge
2454    /// fails here at one site instead of drifting between the CRD
2455    /// wire format and the
2456    /// `core::ConvergencePointType` selector axis that
2457    /// `compliance_binding::PointSelector::ByType` already
2458    /// dispatches against.
2459    #[test]
2460    fn convergence_point_type_bridge_roundtrip_over_all() {
2461        for t in ConvergencePointType::ALL {
2462            let core_t: core::ConvergencePointType = t.into();
2463            let back: ConvergencePointType = core_t.into();
2464            assert_eq!(back, t, "bridge round-trip failed for {t:?}");
2465        }
2466    }
2467
2468    // ── closed-set algebra contracts for Arity ───────────────────
2469
2470    /// `ALL` is the source of truth — pin its closure so a variant
2471    /// added without an `ALL` entry fails here. The arity is asserted
2472    /// by the `[Self; 2]` array type itself.
2473    #[test]
2474    fn arity_all_is_unique_and_complete() {
2475        let mut seen = std::collections::HashSet::new();
2476        for a in Arity::ALL {
2477            assert!(seen.insert(a), "duplicate variant in ALL: {a:?}");
2478        }
2479        assert_eq!(seen.len(), Arity::ALL.len());
2480    }
2481
2482    /// The Display impl IS `as_str` — pinning this lets future
2483    /// callers reach for either projection without drift. No serde
2484    /// matching here because `Arity` is a typed projection, not a
2485    /// CRD-facing enum — it never crosses the wire. Routed through
2486    /// the substrate-wide [`crate::tagged_union::assert_display_matches_label`]
2487    /// primitive so the sweep body lives at ONE substrate site rather
2488    /// than restated per-implementor. Also exercised through the
2489    /// substrate-wide `every_production_display_impl_binds_through_the_testkit_primitive`
2490    /// sweep so a per-crate test-site drop cannot silently disable the
2491    /// check.
2492    #[test]
2493    fn arity_display_matches_as_str() {
2494        crate::tagged_union::assert_display_matches_label::<Arity>();
2495    }
2496
2497    /// PREDICATE CONTRACT: `is_one` is true exactly for `Arity::One`.
2498    /// The disjointness against `Many` is structural (only two
2499    /// variants) but pinning the codomain here means a future
2500    /// `Arity::Zero` variant must declare its own `is_one` arm
2501    /// deliberately rather than silently defaulting through a
2502    /// non-closed-set match.
2503    #[test]
2504    fn arity_is_one_predicate_truth_table() {
2505        assert!(Arity::One.is_one());
2506        assert!(!Arity::Many.is_one());
2507    }
2508
2509    // ── closed-set algebra contracts for SubstrateType
2510    //    (ALL × as_str × FromStr × predicate triple × bridge) ─────────
2511
2512    /// Structural well-formedness of [`SubstrateType`] as a
2513    /// [`tatara_lisp::ClosedSet`] implementor — see
2514    /// [`convergence_point_type_is_well_formed_closed_set`] for the
2515    /// canonical lift narrative. Replaces
2516    /// `substrate_type_all_is_unique_and_complete` +
2517    /// `substrate_type_roundtrip_via_as_str` + the empty-input arm
2518    /// of `unknown_substrate_type_errors`.
2519    #[test]
2520    fn substrate_type_is_well_formed_closed_set() {
2521        tatara_closed_set::assert_closed_set_well_formed::<SubstrateType>();
2522    }
2523
2524    /// CANONICAL-KEY CONTRACT: `as_str` matches serde's PascalCase
2525    /// output verbatim for every variant. A future variant rename
2526    /// (or an `as_str` arm typo) lands here at one site, instead of
2527    /// drifting between the typed surface, the CRD enum, and the
2528    /// YAML wire format the reconciler reads from
2529    /// `spec.classification.substrate`.
2530    #[test]
2531    fn substrate_type_as_str_matches_serde() {
2532        crate::tagged_union::assert_label_matches_serde_serialization::<SubstrateType>();
2533    }
2534
2535    /// The Display impl IS `as_str` — pinning this lets future
2536    /// callers reach for either projection without drift. Any
2537    /// operator-facing `substrate={kind}` diagnostic that composes
2538    /// through Display inherits the canonical wire-format string
2539    /// automatically.
2540    #[test]
2541    fn substrate_type_display_matches_as_str() {
2542        crate::tagged_union::assert_display_matches_label::<SubstrateType>();
2543    }
2544
2545    /// `FromStr` rejects strings outside the canonical projection —
2546    /// lowercased / typo / cross-axis-leaked — and the error echoes
2547    /// the input verbatim so the operator-facing diagnostic surfaces
2548    /// the bad value, not a normalized form. The empty-input arm is
2549    /// pinned by [`substrate_type_is_well_formed_closed_set`] via
2550    /// the `tatara_lisp::ClosedSet` testkit; the cases here pin the
2551    /// verbatim-echo contract on the [`UnknownSubstrateType`]
2552    /// newtype, which the trait's `make_unknown` can't see.
2553    #[test]
2554    fn unknown_substrate_type_errors() {
2555        for bad in [
2556            "compute",  // lowercased
2557            "COMPUTE",  // uppercased
2558            "Computte", // typo
2559            "Database", "Steady",   // PoolPhase-axis leak
2560            "Pii",      // DataClassification-axis leak
2561            "Attested", // ProcessPhase-axis leak
2562            "Gate",     // ConvergencePointType-axis leak
2563            "Monotone", // CalmClassification-axis leak
2564            "PromQL",   // ConditionKind-axis leak
2565        ] {
2566            let err = SubstrateType::from_str(bad).unwrap_err();
2567            assert_eq!(err.0, bad, "error payload should echo input verbatim");
2568        }
2569    }
2570
2571    // `unknown_substrate_type_message_matches_substrate_convention`
2572    // removed — clause (5) of
2573    // `tatara_closed_set::assert_closed_set_well_formed::<SubstrateType>()`
2574    // verifies the substrate-wide `"unknown {SET_LABEL}: {input}"`
2575    // shape generically (called from
2576    // `substrate_type_is_well_formed_closed_set` above); the
2577    // `SET_LABEL` projection is pinned by
2578    // `tatara_lisp_derive::pascal_to_spaced_lowercase_tests`.
2579
2580    /// TRUTH-TABLE CONTRACT: the predicate triple agrees with the
2581    /// documented per-variant plane role. Pinning this table at one
2582    /// site means any future compliance-baseline selector reads the
2583    /// same projection that the reconciler stamps on the CRD.
2584    #[test]
2585    fn substrate_type_predicate_truth_tables() {
2586        // Resource plane: you allocate budgets from it.
2587        for t in [
2588            SubstrateType::Financial,
2589            SubstrateType::Compute,
2590            SubstrateType::Network,
2591            SubstrateType::Storage,
2592        ] {
2593            assert!(t.is_resource(), "{t:?} should be a resource substrate");
2594            assert!(!t.is_policy(), "{t:?} should not be a policy substrate");
2595            assert!(
2596                !t.is_telemetry(),
2597                "{t:?} should not be a telemetry substrate"
2598            );
2599        }
2600
2601        // Policy plane: it gates access for other workloads.
2602        for t in [
2603            SubstrateType::Security,
2604            SubstrateType::Identity,
2605            SubstrateType::Regulatory,
2606        ] {
2607            assert!(!t.is_resource(), "{t:?} should not be a resource substrate");
2608            assert!(t.is_policy(), "{t:?} should be a policy substrate");
2609            assert!(
2610                !t.is_telemetry(),
2611                "{t:?} should not be a telemetry substrate"
2612            );
2613        }
2614
2615        // Telemetry plane: it observes other workloads.
2616        assert!(!SubstrateType::Observability.is_resource());
2617        assert!(!SubstrateType::Observability.is_policy());
2618        assert!(SubstrateType::Observability.is_telemetry());
2619    }
2620
2621    /// COVERAGE CONTRACT: every variant lands in *exactly one* of
2622    /// the three plane buckets — resource, policy, or telemetry.
2623    /// Pins the three buckets at their declared cardinalities (4,
2624    /// 3, 1 — sum to `ALL.len()`) so a future variant lands
2625    /// somewhere deliberately. No variant returns true from more
2626    /// than one predicate; no variant returns false from all three.
2627    #[test]
2628    fn substrate_type_buckets_cover_every_variant() {
2629        let mut resource = 0u32;
2630        let mut policy = 0u32;
2631        let mut telemetry = 0u32;
2632        for t in SubstrateType::ALL {
2633            let buckets = [t.is_resource(), t.is_policy(), t.is_telemetry()];
2634            let hits: u32 = buckets.iter().map(|b| u32::from(*b)).sum();
2635            assert_eq!(
2636                hits, 1,
2637                "{t:?} landed in {hits} buckets: {buckets:?} (must be exactly one)"
2638            );
2639            if t.is_resource() {
2640                resource += 1;
2641            }
2642            if t.is_policy() {
2643                policy += 1;
2644            }
2645            if t.is_telemetry() {
2646                telemetry += 1;
2647            }
2648        }
2649        assert_eq!(
2650            resource, 4,
2651            "resource bucket: Financial + Compute + Network + Storage"
2652        );
2653        assert_eq!(policy, 3, "policy bucket: Security + Identity + Regulatory");
2654        assert_eq!(telemetry, 1, "telemetry bucket: Observability");
2655        assert_eq!(
2656            resource + policy + telemetry,
2657            SubstrateType::ALL.len() as u32
2658        );
2659    }
2660
2661    /// BRIDGE ROUND-TRIP CONTRACT: every variant survives the
2662    /// CRD-facing (`PascalCase`) ↔ tatara-core (`snake_case`)
2663    /// `From` hop. Today the bridge is two hand-written 8-arm
2664    /// matches in this file; pinning the round-trip over `ALL`
2665    /// means a future variant added without extending the bridge
2666    /// fails here at one site instead of drifting between the CRD
2667    /// wire format and the `core::SubstrateType` selector axis
2668    /// that `compliance_binding::PointSelector::BySubstrate`
2669    /// already dispatches against.
2670    #[test]
2671    fn substrate_type_bridge_roundtrip_over_all() {
2672        for t in SubstrateType::ALL {
2673            let core_t: core::SubstrateType = t.into();
2674            let back: SubstrateType = core_t.into();
2675            assert_eq!(back, t, "bridge round-trip failed for {t:?}");
2676        }
2677    }
2678
2679    // ── closed-set algebra contracts for CalmClassification
2680    //    (ALL × as_str × FromStr × requires_coordination × bridge) ─────
2681
2682    /// Structural well-formedness of [`CalmClassification`] as a
2683    /// [`tatara_lisp::ClosedSet`] implementor — see
2684    /// [`convergence_point_type_is_well_formed_closed_set`] for the
2685    /// canonical lift narrative. Replaces
2686    /// `calm_classification_all_is_unique_and_complete` +
2687    /// `calm_classification_roundtrip_via_as_str` + the empty-input
2688    /// arm of `unknown_calm_classification_errors`.
2689    #[test]
2690    fn calm_classification_is_well_formed_closed_set() {
2691        tatara_closed_set::assert_closed_set_well_formed::<CalmClassification>();
2692    }
2693
2694    /// CANONICAL-KEY CONTRACT: `as_str` matches serde's PascalCase
2695    /// output verbatim for every variant. A future variant rename
2696    /// (or an `as_str` arm typo) lands here at one site, instead of
2697    /// drifting between the typed surface, the CRD enum, and the
2698    /// YAML wire format the reconciler reads from
2699    /// `spec.classification.calm`.
2700    #[test]
2701    fn calm_classification_as_str_matches_serde() {
2702        crate::tagged_union::assert_label_matches_serde_serialization::<CalmClassification>();
2703    }
2704
2705    /// The Display impl IS `as_str` — pinning this lets future
2706    /// callers reach for either projection without drift. Any
2707    /// operator-facing `calm={kind}` diagnostic that composes
2708    /// through Display inherits the canonical wire-format string
2709    /// automatically.
2710    #[test]
2711    fn calm_classification_display_matches_as_str() {
2712        crate::tagged_union::assert_display_matches_label::<CalmClassification>();
2713    }
2714
2715    /// `FromStr` rejects strings outside the canonical projection —
2716    /// lowercased / typo / cross-axis-leaked — and the error echoes
2717    /// the input verbatim so the operator-facing diagnostic surfaces
2718    /// the bad value, not a normalized form. The empty-input arm is
2719    /// pinned by [`calm_classification_is_well_formed_closed_set`]
2720    /// via the `tatara_lisp::ClosedSet` testkit; the cases here pin
2721    /// the verbatim-echo contract on the
2722    /// [`UnknownCalmClassification`] newtype, which the trait's
2723    /// `make_unknown` can't see.
2724    #[test]
2725    fn unknown_calm_classification_errors() {
2726        for bad in [
2727            "monotone",     // lowercased
2728            "MONOTONE",     // uppercased
2729            "Mono",         // typo
2730            "non_monotone", // core's snake_case form (must not cross axes)
2731            "non-monotone", // dashed
2732            "Monotonic",    // close-typo
2733            "Steady",       // PoolPhase-axis leak
2734            "Pii",          // DataClassification-axis leak
2735            "Attested",     // ProcessPhase-axis leak
2736            "Compute",      // SubstrateType-axis leak
2737            "Gate",         // ConvergencePointType-axis leak
2738            "PromQL",       // ConditionKind-axis leak
2739        ] {
2740            let err = CalmClassification::from_str(bad).unwrap_err();
2741            assert_eq!(err.0, bad, "error payload should echo input verbatim");
2742        }
2743    }
2744
2745    // `unknown_calm_classification_message_matches_substrate_convention`
2746    // removed — clause (5) of
2747    // `tatara_closed_set::assert_closed_set_well_formed::<CalmClassification>()`
2748    // verifies the substrate-wide `"unknown {SET_LABEL}: {input}"`
2749    // shape generically (called from
2750    // `calm_classification_is_well_formed_closed_set` above); the
2751    // `SET_LABEL` projection is pinned by
2752    // `tatara_lisp_derive::pascal_to_spaced_lowercase_tests`.
2753
2754    /// CALM-THEOREM TRUTH-TABLE CONTRACT: `requires_coordination`
2755    /// implements the biconditional half of Hellerstein's CALM
2756    /// theorem — `Monotone ⇒ false` and `NonMonotone ⇒ true`.
2757    /// Pinning this table at one site means any future reconciler
2758    /// dispatch that picks between Raft writes and gossip
2759    /// propagation reads the same projection the lattice ordering
2760    /// (`Monotone ≤ NonMonotone`) does. A future variant that
2761    /// flipped this mapping would have to renumber every consumer
2762    /// deliberately rather than silently shipping a non-monotone
2763    /// operation onto the no-coordination path.
2764    #[test]
2765    fn calm_classification_requires_coordination_truth_table() {
2766        assert!(!CalmClassification::Monotone.requires_coordination());
2767        assert!(CalmClassification::NonMonotone.requires_coordination());
2768    }
2769
2770    /// COVERAGE CONTRACT: every variant lands in exactly one of two
2771    /// coordination buckets — no-coordination (`Monotone`) or
2772    /// requires-coordination (`NonMonotone`). Pins the two buckets
2773    /// at their declared cardinalities (1, 1 — sum to `ALL.len()`)
2774    /// so a future variant lands somewhere deliberately. The
2775    /// biconditional structure of the CALM theorem makes this
2776    /// partition exhaustive by construction.
2777    #[test]
2778    fn calm_classification_buckets_cover_every_variant() {
2779        let mut no_coord = 0u32;
2780        let mut coord = 0u32;
2781        for c in CalmClassification::ALL {
2782            if c.requires_coordination() {
2783                coord += 1;
2784            } else {
2785                no_coord += 1;
2786            }
2787        }
2788        assert_eq!(no_coord, 1, "no-coordination bucket: Monotone");
2789        assert_eq!(coord, 1, "requires-coordination bucket: NonMonotone");
2790        assert_eq!(no_coord + coord, CalmClassification::ALL.len() as u32);
2791    }
2792
2793    /// DEFAULT-AGREEMENT CONTRACT: `CalmClassification::default()`
2794    /// returns `Monotone` (the variant tagged `#[default]`) AND that
2795    /// variant lands in the no-coordination bucket. A future
2796    /// `#[default]` rename without flipping the predicate fails
2797    /// here — the default for an under-specified Process must
2798    /// remain the no-coordination side so that an unannotated
2799    /// Process can't silently demand Raft writes the reconciler
2800    /// isn't configured to provide.
2801    #[test]
2802    fn calm_classification_default_is_monotone_no_coordination() {
2803        let c = CalmClassification::default();
2804        assert_eq!(c, CalmClassification::Monotone);
2805        assert!(!c.requires_coordination());
2806    }
2807
2808    /// BRIDGE ROUND-TRIP CONTRACT: every variant survives the
2809    /// CRD-facing (`PascalCase`) ↔ tatara-core (`snake_case`)
2810    /// `From` hop. Today the bridge is two hand-written 2-arm
2811    /// matches in this file; pinning the round-trip over `ALL`
2812    /// means a future variant added without extending the bridge
2813    /// fails here at one site instead of drifting between the CRD
2814    /// wire format and the `core::CalmClassification` selector
2815    /// axis. Closes the asymmetry that pre-lift had a
2816    /// `From<CalmClassification> for core::CalmClassification`
2817    /// forward bridge but no reverse — symmetric to every other
2818    /// classification-axis bridge in this file.
2819    #[test]
2820    fn calm_classification_bridge_roundtrip_over_all() {
2821        for c in CalmClassification::ALL {
2822            let core_c: core::CalmClassification = c.into();
2823            let back: CalmClassification = core_c.into();
2824            assert_eq!(back, c, "bridge round-trip failed for {c:?}");
2825        }
2826    }
2827
2828    // ── closed-set algebra contracts for OptimizationDirection
2829    //    (ALL × as_str × FromStr × prefers_lower × is_improvement) ───
2830
2831    /// Structural well-formedness of [`OptimizationDirection`] as a
2832    /// [`tatara_lisp::ClosedSet`] implementor — see
2833    /// [`convergence_point_type_is_well_formed_closed_set`] for the
2834    /// canonical lift narrative. Replaces
2835    /// `optimization_direction_all_is_unique_and_complete` +
2836    /// `optimization_direction_roundtrip_via_as_str` + the empty-
2837    /// input arm of `unknown_optimization_direction_errors`.
2838    #[test]
2839    fn optimization_direction_is_well_formed_closed_set() {
2840        tatara_closed_set::assert_closed_set_well_formed::<OptimizationDirection>();
2841    }
2842
2843    /// CANONICAL-KEY CONTRACT: `as_str` matches serde's PascalCase
2844    /// output verbatim for every variant. A future variant rename
2845    /// (or an `as_str` arm typo) lands here at one site, instead of
2846    /// drifting between the typed surface, the CRD enum, and the
2847    /// YAML wire format the reconciler reads from
2848    /// `spec.classification.horizon.direction`.
2849    #[test]
2850    fn optimization_direction_as_str_matches_serde() {
2851        crate::tagged_union::assert_label_matches_serde_serialization::<OptimizationDirection>();
2852    }
2853
2854    /// The Display impl IS `as_str` — pinning this lets future
2855    /// callers reach for either projection without drift. Any
2856    /// operator-facing `direction={kind}` diagnostic that composes
2857    /// through Display inherits the canonical wire-format string
2858    /// automatically.
2859    #[test]
2860    fn optimization_direction_display_matches_as_str() {
2861        crate::tagged_union::assert_display_matches_label::<OptimizationDirection>();
2862    }
2863
2864    /// `FromStr` rejects strings outside the canonical projection —
2865    /// lowercased / typo / cross-axis-leaked — and the error echoes
2866    /// the input verbatim so the operator-facing diagnostic surfaces
2867    /// the bad value, not a normalized form. The empty-input arm is
2868    /// pinned by [`optimization_direction_is_well_formed_closed_set`]
2869    /// via the `tatara_lisp::ClosedSet` testkit; the cases here pin
2870    /// the verbatim-echo contract on the
2871    /// [`UnknownOptimizationDirection`] newtype, which the trait's
2872    /// `make_unknown` can't see.
2873    #[test]
2874    fn unknown_optimization_direction_errors() {
2875        for bad in [
2876            "minimize", // lowercased
2877            "MINIMIZE", // uppercased
2878            "Minimze",  // typo
2879            "Lower",    // synonym, not canonical
2880            "Higher",   // synonym, not canonical
2881            "Asc",      // wire-leak from sort-order axis
2882            "Desc",     // wire-leak from sort-order axis
2883            "Bounded",  // HorizonKind-axis leak
2884            "Monotone", // CalmClassification-axis leak
2885            "Steady",   // PoolPhase-axis leak
2886            "Pii",      // DataClassification-axis leak
2887            "Attested", // ProcessPhase-axis leak
2888            "Compute",  // SubstrateType-axis leak
2889            "Gate",     // ConvergencePointType-axis leak
2890            "PromQL",   // ConditionKind-axis leak
2891        ] {
2892            let err = OptimizationDirection::from_str(bad).unwrap_err();
2893            assert_eq!(err.0, bad, "error payload should echo input verbatim");
2894        }
2895    }
2896
2897    // `unknown_optimization_direction_message_matches_substrate_convention`
2898    // removed — clause (5) of
2899    // `tatara_closed_set::assert_closed_set_well_formed::<OptimizationDirection>()`
2900    // verifies the substrate-wide `"unknown {SET_LABEL}: {input}"`
2901    // shape generically (called from
2902    // `optimization_direction_is_well_formed_closed_set` above); the
2903    // `SET_LABEL` projection is pinned by
2904    // `tatara_lisp_derive::pascal_to_spaced_lowercase_tests`.
2905
2906    /// TRUTH-TABLE CONTRACT: `prefers_lower` is the boolean
2907    /// partition `Minimize ⇒ true`, `Maximize ⇒ false`. Pinning this
2908    /// table at one site means any future dispatch on per-direction
2909    /// polarity (rate-window evaluator, breathe-band regression
2910    /// detector) reads the same projection rather than re-deriving
2911    /// from the variant name. Mirrors
2912    /// [`CalmClassification::requires_coordination`]'s truth-table
2913    /// shape.
2914    #[test]
2915    fn optimization_direction_prefers_lower_truth_table() {
2916        assert!(OptimizationDirection::Minimize.prefers_lower());
2917        assert!(!OptimizationDirection::Maximize.prefers_lower());
2918    }
2919
2920    /// COVERAGE CONTRACT: every variant lands in exactly one of two
2921    /// polarity buckets — prefers-lower (`Minimize`) or
2922    /// prefers-higher (`Maximize`). Pins the two buckets at their
2923    /// declared cardinalities (1, 1 — sum to `ALL.len()`) so a
2924    /// future variant lands somewhere deliberately.
2925    #[test]
2926    fn optimization_direction_buckets_cover_every_variant() {
2927        let mut lower = 0u32;
2928        let mut higher = 0u32;
2929        for d in OptimizationDirection::ALL {
2930            if d.prefers_lower() {
2931                lower += 1;
2932            } else {
2933                higher += 1;
2934            }
2935        }
2936        assert_eq!(lower, 1, "prefers-lower bucket: Minimize");
2937        assert_eq!(higher, 1, "prefers-higher bucket: Maximize");
2938        assert_eq!(lower + higher, OptimizationDirection::ALL.len() as u32);
2939    }
2940
2941    /// LOAD-BEARING TRUTH-TABLE: `is_improvement` answers "is `after`
2942    /// strictly better than `before` under this direction?" for the
2943    /// canonical samples. Pins the strict-improvement semantic at
2944    /// one site so a future rate-window evaluator or breathe-band
2945    /// regression detector reads the same projection that the
2946    /// asymptotic-health probe writes.
2947    #[test]
2948    fn optimization_direction_is_improvement_truth_table() {
2949        // Minimize: lower-is-better
2950        assert!(OptimizationDirection::Minimize.is_improvement(10.0, 5.0));
2951        assert!(!OptimizationDirection::Minimize.is_improvement(5.0, 10.0));
2952
2953        // Maximize: higher-is-better
2954        assert!(OptimizationDirection::Maximize.is_improvement(5.0, 10.0));
2955        assert!(!OptimizationDirection::Maximize.is_improvement(10.0, 5.0));
2956    }
2957
2958    /// NO-OP CONTRACT: a sample equal to the previous one is NOT an
2959    /// improvement under either direction. Pinning this guarantees
2960    /// a flatlined rate-window evaluator doesn't silently keep
2961    /// claiming "still improving" forever and skipping the
2962    /// healthy-rate-threshold gate.
2963    #[test]
2964    fn optimization_direction_no_op_is_not_improvement() {
2965        for d in OptimizationDirection::ALL {
2966            assert!(
2967                !d.is_improvement(7.0, 7.0),
2968                "{d:?}: equal samples must not count as improvement",
2969            );
2970            assert!(
2971                !d.is_improvement(0.0, 0.0),
2972                "{d:?}: zero/zero must not count as improvement",
2973            );
2974        }
2975    }
2976
2977    /// NaN CONTRACT: NaN on either operand short-circuits to `false`
2978    /// (no improvement claim from indeterminate data) via the
2979    /// standard `PartialOrd` behavior. Without this, a rate-window
2980    /// evaluator that sampled a NaN partway through (a transient
2981    /// metric-scrape failure) would either panic on an `Ord`
2982    /// comparison or — worse — silently claim improvement on the
2983    /// next valid sample by treating NaN as the worst case.
2984    #[test]
2985    fn optimization_direction_nan_is_not_improvement() {
2986        let nan = f64::NAN;
2987        for d in OptimizationDirection::ALL {
2988            assert!(
2989                !d.is_improvement(nan, 1.0),
2990                "{d:?}: NaN before must not count as improvement",
2991            );
2992            assert!(
2993                !d.is_improvement(1.0, nan),
2994                "{d:?}: NaN after must not count as improvement",
2995            );
2996            assert!(
2997                !d.is_improvement(nan, nan),
2998                "{d:?}: NaN/NaN must not count as improvement",
2999            );
3000        }
3001    }
3002
3003    /// ANTISYMMETRY CONTRACT: for distinct finite samples,
3004    /// `is_improvement(a, b)` xor `is_improvement(b, a)` —
3005    /// exactly one direction of the pair counts as improvement.
3006    /// This is the algebraic shape every asymptotic-health
3007    /// rate-window evaluator depends on to avoid double-counting
3008    /// an improvement as a regression on the reverse traversal.
3009    /// A future variant that returned `true` for both directions
3010    /// (or `false` for both, the equal-sample case) would FAIL
3011    /// here, forcing the author to extend the consumer dispatch
3012    /// deliberately.
3013    #[test]
3014    fn optimization_direction_is_improvement_is_antisymmetric() {
3015        let pairs = [(1.0_f64, 2.0_f64), (0.0, 100.0), (-3.5, 3.5), (1e9, 1e-9)];
3016        for d in OptimizationDirection::ALL {
3017            for (a, b) in pairs {
3018                assert!(a != b, "test fixture requires distinct samples");
3019                assert!(
3020                    d.is_improvement(a, b) ^ d.is_improvement(b, a),
3021                    "{d:?}: antisymmetry violated on ({a}, {b})",
3022                );
3023            }
3024        }
3025    }
3026
3027    /// DEFAULT-AGREEMENT CONTRACT:
3028    /// `OptimizationDirection::default()` returns `Minimize` (the
3029    /// variant tagged `#[default]`), AND that variant lands in the
3030    /// prefers-lower bucket. A future `#[default]` rename without
3031    /// flipping the predicate fails here — `Minimize` is the
3032    /// canonical default for distributed-systems asymptotic
3033    /// optimization (cost / latency / error rate), so an
3034    /// unannotated metric must not silently flip the rate-window
3035    /// evaluator's polarity. This is also the same value the
3036    /// `Horizon → ConvergenceHorizon` bridge falls back to when
3037    /// `direction` is unset, so pinning the default here pins the
3038    /// bridge's behavior at one site.
3039    #[test]
3040    fn optimization_direction_default_is_minimize_prefers_lower() {
3041        let d = OptimizationDirection::default();
3042        assert_eq!(d, OptimizationDirection::Minimize);
3043        assert!(d.prefers_lower());
3044    }
3045
3046    /// BRIDGE ROUND-TRIP CONTRACT: every variant survives the
3047    /// CRD-facing (`PascalCase`) ↔ tatara-core (`snake_case`)
3048    /// `From` hop. Pre-lift the bridge was a one-way
3049    /// `From<OptimizationDirection> for core::OptimizationDirection`
3050    /// with no reverse — asymmetric to every other classification-
3051    /// axis bridge in this file. Pinning the round-trip over `ALL`
3052    /// means a future variant added without extending the bridge
3053    /// fails here at one site instead of drifting between the CRD
3054    /// wire format and `core::OptimizationDirection`.
3055    #[test]
3056    fn optimization_direction_bridge_roundtrip_over_all() {
3057        for d in OptimizationDirection::ALL {
3058            let core_d: core::OptimizationDirection = d.into();
3059            let back: OptimizationDirection = core_d.into();
3060            assert_eq!(back, d, "bridge round-trip failed for {d:?}");
3061        }
3062    }
3063
3064    // ── closed-set algebra contracts for HorizonKind
3065    //    (ALL × as_str × FromStr × terminates × requires_metric_axes) ──
3066
3067    /// Structural well-formedness of [`HorizonKind`] as a
3068    /// [`tatara_lisp::ClosedSet`] implementor — see
3069    /// [`convergence_point_type_is_well_formed_closed_set`] for the
3070    /// canonical lift narrative. Replaces
3071    /// `horizon_kind_all_is_unique_and_complete` +
3072    /// `horizon_kind_roundtrip_via_as_str` + the empty-input arm of
3073    /// `unknown_horizon_kind_errors`.
3074    #[test]
3075    fn horizon_kind_is_well_formed_closed_set() {
3076        tatara_closed_set::assert_closed_set_well_formed::<HorizonKind>();
3077    }
3078
3079    /// CANONICAL-KEY CONTRACT: `as_str` matches serde's PascalCase
3080    /// output verbatim for every variant. A future variant rename
3081    /// (or an `as_str` arm typo) lands here at one site, instead of
3082    /// drifting between the typed surface, the CRD enum, and the
3083    /// YAML wire format the reconciler stamps on
3084    /// `spec.classification.horizon.kind`.
3085    #[test]
3086    fn horizon_kind_as_str_matches_serde() {
3087        crate::tagged_union::assert_label_matches_serde_serialization::<HorizonKind>();
3088    }
3089
3090    /// The Display impl IS `as_str` — pinning this lets future
3091    /// callers reach for either projection without drift. Any
3092    /// operator-facing `horizon.kind={kind}` diagnostic that
3093    /// composes through Display inherits the canonical wire-format
3094    /// string automatically.
3095    #[test]
3096    fn horizon_kind_display_matches_as_str() {
3097        crate::tagged_union::assert_display_matches_label::<HorizonKind>();
3098    }
3099
3100    /// `FromStr` rejects strings outside the canonical projection —
3101    /// lowercased / typo / cross-axis-leaked — and the error echoes
3102    /// the input verbatim so the operator-facing diagnostic surfaces
3103    /// the bad value, not a normalized form. The empty-input arm is
3104    /// pinned by [`horizon_kind_is_well_formed_closed_set`] via the
3105    /// `tatara_lisp::ClosedSet` testkit; the cases here pin the
3106    /// verbatim-echo contract on the [`UnknownHorizonKind`] newtype,
3107    /// which the trait's `make_unknown` can't see.
3108    #[test]
3109    fn unknown_horizon_kind_errors() {
3110        for bad in [
3111            "bounded",   // lowercased
3112            "BOUNDED",   // uppercased
3113            "Boundd",    // typo
3114            "Finite",    // synonym, not canonical
3115            "Perpetual", // synonym, not canonical
3116            "Infinite",  // synonym, not canonical
3117            "Minimize",  // OptimizationDirection-axis leak
3118            "Monotone",  // CalmClassification-axis leak
3119            "Pii",       // DataClassification-axis leak
3120            "Steady",    // PoolPhase-axis leak
3121            "Attested",  // ProcessPhase-axis leak
3122            "Compute",   // SubstrateType-axis leak
3123            "Gate",      // ConvergencePointType-axis leak
3124            "PromQL",    // ConditionKind-axis leak
3125        ] {
3126            let err = HorizonKind::from_str(bad).unwrap_err();
3127            assert_eq!(err.0, bad, "error payload should echo input verbatim");
3128        }
3129    }
3130
3131    // `unknown_horizon_kind_message_matches_substrate_convention`
3132    // removed — clause (5) of
3133    // `tatara_closed_set::assert_closed_set_well_formed::<HorizonKind>()`
3134    // verifies the substrate-wide `"unknown {SET_LABEL}: {input}"`
3135    // shape generically (called from
3136    // `horizon_kind_is_well_formed_closed_set` above); the
3137    // `SET_LABEL` projection is pinned by
3138    // `tatara_lisp_derive::pascal_to_spaced_lowercase_tests`.
3139
3140    /// LOAD-BEARING TRUTH-TABLE: `terminates` is the boolean
3141    /// partition `Bounded ⇒ true`, `Asymptotic ⇒ false`. Pinning
3142    /// this table at one site means any future scheduler asking
3143    /// "will this Process reach `Reaped` via natural termination?"
3144    /// reads the same projection that the lattice ordering encodes
3145    /// (Bounded ≤ Asymptotic BECAUSE the bounded horizon strictly
3146    /// refines the asymptotic one by also terminating).
3147    #[test]
3148    fn horizon_kind_terminates_truth_table() {
3149        assert!(HorizonKind::Bounded.terminates());
3150        assert!(!HorizonKind::Asymptotic.terminates());
3151    }
3152
3153    /// LOAD-BEARING TRUTH-TABLE: `requires_metric_axes` is the
3154    /// boolean partition `Bounded ⇒ false`, `Asymptotic ⇒ true` —
3155    /// the typed image of the optionality the [`Horizon`] struct
3156    /// encodes via its three `Option<…>` fields (`metric`,
3157    /// `direction`, `healthy_rate_threshold`). The implicit
3158    /// "Asymptotic only" invariant in the field docs is now a
3159    /// checkable per-kind predicate. Pinning this table at one site
3160    /// means any future horizon-shape validator (CRD admission,
3161    /// `tatara-check` form linter, Lisp authoring-time predicate)
3162    /// reads the same projection.
3163    #[test]
3164    fn horizon_kind_requires_metric_axes_truth_table() {
3165        assert!(!HorizonKind::Bounded.requires_metric_axes());
3166        assert!(HorizonKind::Asymptotic.requires_metric_axes());
3167    }
3168
3169    /// COVERAGE CONTRACT: every variant lands in exactly one of two
3170    /// termination buckets — terminating (`Bounded`) or perpetual
3171    /// (`Asymptotic`). Pins the two buckets at their declared
3172    /// cardinalities (1, 1 — sum to `ALL.len()`) so a future variant
3173    /// lands somewhere deliberately.
3174    #[test]
3175    fn horizon_kind_buckets_cover_every_variant() {
3176        let mut terminating = 0u32;
3177        let mut perpetual = 0u32;
3178        for k in HorizonKind::ALL {
3179            if k.terminates() {
3180                terminating += 1;
3181            } else {
3182                perpetual += 1;
3183            }
3184        }
3185        assert_eq!(terminating, 1, "terminating bucket: Bounded");
3186        assert_eq!(perpetual, 1, "perpetual bucket: Asymptotic");
3187        assert_eq!(terminating + perpetual, HorizonKind::ALL.len() as u32);
3188    }
3189
3190    /// ANTISYMMETRY CONTRACT: for every variant, exactly one of
3191    /// `(terminates, requires_metric_axes)` is true — the two
3192    /// predicates carve the variants into complementary buckets
3193    /// (terminating ↔ no metric axes; perpetual ↔ requires metric
3194    /// axes). A future variant that returned `true` for both (a
3195    /// terminating horizon that nonetheless tracks an asymptotic
3196    /// metric) or `false` for both (an inert horizon with no
3197    /// termination AND no metric signal — there'd be nothing to
3198    /// observe) would fail here, forcing the author to extend
3199    /// either the predicates or the [`Horizon`] struct's
3200    /// optionality contract deliberately.
3201    #[test]
3202    fn horizon_kind_terminate_xor_requires_metric_axes() {
3203        for k in HorizonKind::ALL {
3204            assert!(
3205                k.terminates() ^ k.requires_metric_axes(),
3206                "{k:?}: terminates() XOR requires_metric_axes() must hold",
3207            );
3208        }
3209    }
3210
3211    /// DEFAULT-AGREEMENT CONTRACT: `HorizonKind::default()` returns
3212    /// `Bounded` (the variant tagged `#[default]`), AND that
3213    /// variant lands in the terminating bucket. A future
3214    /// `#[default]` rename without flipping the predicate fails
3215    /// here — `Bounded` is the canonical default for a convergence
3216    /// horizon (a point with no asymptotic axes declared should
3217    /// terminate naturally, not silently flip into a perpetual
3218    /// rate-window evaluator with zero threshold). This is also
3219    /// the same value `Horizon::default()` carries, so pinning the
3220    /// default here pins the struct-default behavior at one site.
3221    #[test]
3222    fn horizon_kind_default_is_bounded_terminates() {
3223        let k = HorizonKind::default();
3224        assert_eq!(k, HorizonKind::Bounded);
3225        assert!(k.terminates());
3226        assert!(!k.requires_metric_axes());
3227    }
3228
3229    /// HORIZON ↔ KIND AGREEMENT: every variant in `HorizonKind::ALL`
3230    /// composes with the existing [`Horizon::bounded`] /
3231    /// [`Horizon::asymptotic`] constructors to produce a `Horizon`
3232    /// whose `kind` matches AND whose `Option<…>` fields agree
3233    /// with `requires_metric_axes`. Pins the implicit contract
3234    /// between the kind discriminator and the optionality at one
3235    /// site — a future kind added without extending either the
3236    /// constructors or `requires_metric_axes` fails here before
3237    /// drifting between the typed surface and the documented
3238    /// "Asymptotic only" field invariant.
3239    #[test]
3240    fn horizon_kind_agrees_with_struct_optionality() {
3241        let bounded = Horizon::bounded();
3242        assert_eq!(bounded.kind, HorizonKind::Bounded);
3243        assert!(!bounded.kind.requires_metric_axes());
3244        assert!(bounded.metric.is_none());
3245        assert!(bounded.direction.is_none());
3246        assert!(bounded.healthy_rate_threshold.is_none());
3247
3248        let asymp = Horizon::asymptotic("p99_latency", OptimizationDirection::Minimize, 0.1);
3249        assert_eq!(asymp.kind, HorizonKind::Asymptotic);
3250        assert!(asymp.kind.requires_metric_axes());
3251        assert!(asymp.metric.is_some());
3252        assert!(asymp.direction.is_some());
3253        assert!(asymp.healthy_rate_threshold.is_some());
3254    }
3255
3256    // ── scalar-carrier presence probe on Classification × ConvergencePointType ──
3257    //
3258    // Fail-before-pass-after granularity: [`Classification::has_point_type`]
3259    // did not exist before this commit — every consumer of the
3260    // `(Classification, ConvergencePointType) -> bool` scalar-carrier
3261    // probe shape restated the `classification.point_type == kind`
3262    // equality body at its own callsite. Post-lift the shape lives at
3263    // ONE substrate owner and every downstream (the `point-type-<kind>`
3264    // require-tag family in `tatara-check`, future audit dispatchers
3265    // walking [`ConvergencePointType::ALL`], any future CRD-facing
3266    // closed-set discriminator on a required scalar `ProcessSpec` field
3267    // such as `has_substrate`/`has_calm`/`has_data_classification`)
3268    // binds through the SAME `has(kind)` shape the Option-slot
3269    // (`Intent::has`, `Lifetime::has`), slice-level
3270    // (`ConditionSliceExt::has_kind`, `DependsOnSliceExt::has_must_reach`,
3271    // `ComplianceBindingSliceExt::has_verification_phase`,
3272    // `ExportSpecSliceExt::has_{when,channel_kind,report_format,artifact_kind}`),
3273    // and prior scalar-carrier
3274    // (`SignalPolicy::has_sighup_strategy`,
3275    // `EncapsulatesSpec::has_mode`) peers publish.
3276
3277    /// DIAGONAL — for every [`ConvergencePointType`] variant, a
3278    /// [`Classification`] whose `point_type` field is set to that
3279    /// variant returns `true` from `has_point_type` on that same
3280    /// variant AND `false` on every other variant. Sweep the
3281    /// [`ConvergencePointType::ALL`] × ALL cross so a regression that
3282    /// hard-coded the arm to a single variant (silently returning
3283    /// `true` on every populated classification regardless of query
3284    /// kind) or wired the equality to a fixed unrelated field fails
3285    /// HERE at the substrate primitive before landing at the
3286    /// operator-facing checks.lisp surface.
3287    #[test]
3288    fn classification_has_point_type_returns_true_iff_variant_matches() {
3289        for populated in ConvergencePointType::ALL {
3290            let c = Classification {
3291                point_type: populated,
3292                substrate: SubstrateType::Compute,
3293                horizon: Horizon::default(),
3294                calm: CalmClassification::default(),
3295                data_classification: DataClassification::default(),
3296            };
3297            for query in ConvergencePointType::ALL {
3298                assert_eq!(
3299                    c.has_point_type(query),
3300                    query == populated,
3301                    "point_type={populated:?}: query {query:?} classification drifted",
3302                );
3303            }
3304        }
3305    }
3306
3307    /// GATE-COMPUTE BASELINE — the workspace-baseline
3308    /// [`Classification::gate_compute`] shape carries
3309    /// `point_type: Gate`, so `has_point_type` returns `true` on
3310    /// [`ConvergencePointType::Gate`] and `false` on every other of
3311    /// the eight variants. Pins the composition of the substrate's
3312    /// baseline-constructor primitive with the scalar-carrier
3313    /// presence probe — a regression that flipped
3314    /// `gate_compute().point_type` off `Gate` (or wired
3315    /// `has_point_type` to a fixed variant answer) fails here at ONE
3316    /// narrow site before drifting across every unadorned ephemeral
3317    /// env (`default_ephemeral_class`) and every downstream test
3318    /// fixture that keys assertions on the shape.
3319    #[test]
3320    fn classification_gate_compute_has_point_type_gate_only() {
3321        let c = Classification::gate_compute();
3322        for kind in ConvergencePointType::ALL {
3323            let expected = kind == ConvergencePointType::Gate;
3324            assert_eq!(
3325                c.has_point_type(kind),
3326                expected,
3327                "gate_compute (point_type=Gate) must return {expected} for {kind:?}",
3328            );
3329        }
3330    }
3331
3332    // ── scalar-carrier presence probe on Classification × SubstrateType ──
3333    //
3334    // Fail-before-pass-after granularity: [`Classification::has_substrate`]
3335    // did not exist before this commit — every consumer of the
3336    // `(Classification, SubstrateType) -> bool` scalar-carrier probe
3337    // shape restated the `classification.substrate == kind` equality
3338    // body at its own callsite. Post-lift the shape lives at ONE
3339    // substrate owner and every downstream (the `substrate-<kind>`
3340    // require-tag family in `tatara-check`, future audit dispatchers
3341    // walking [`SubstrateType::ALL`], any future CRD-facing closed-set
3342    // discriminator on a required scalar `ProcessSpec` field such as
3343    // `has_calm`/`has_data_classification`) binds through the SAME
3344    // `has(kind)` shape the Option-slot (`Intent::has`, `Lifetime::has`),
3345    // slice-level (`ConditionSliceExt::has_kind`,
3346    // `DependsOnSliceExt::has_must_reach`,
3347    // `ComplianceBindingSliceExt::has_verification_phase`,
3348    // `ExportSpecSliceExt::has_{when,channel_kind,report_format,artifact_kind}`),
3349    // and prior scalar-carrier
3350    // (`SignalPolicy::has_sighup_strategy`,
3351    // `EncapsulatesSpec::has_mode`, `Classification::has_point_type`)
3352    // peers publish.
3353
3354    /// DIAGONAL — for every [`SubstrateType`] variant, a
3355    /// [`Classification`] whose `substrate` field is set to that
3356    /// variant returns `true` from `has_substrate` on that same
3357    /// variant AND `false` on every other variant. Sweep the
3358    /// [`SubstrateType::ALL`] × ALL cross so a regression that
3359    /// hard-coded the arm to a single variant (silently returning
3360    /// `true` on every populated classification regardless of query
3361    /// kind) or wired the equality to a fixed unrelated field (a
3362    /// stray probe on `classification.point_type`) fails HERE at the
3363    /// substrate primitive before landing at the operator-facing
3364    /// checks.lisp surface.
3365    #[test]
3366    fn classification_has_substrate_returns_true_iff_variant_matches() {
3367        for populated in SubstrateType::ALL {
3368            let c = Classification {
3369                point_type: ConvergencePointType::Gate,
3370                substrate: populated,
3371                horizon: Horizon::default(),
3372                calm: CalmClassification::default(),
3373                data_classification: DataClassification::default(),
3374            };
3375            for query in SubstrateType::ALL {
3376                assert_eq!(
3377                    c.has_substrate(query),
3378                    query == populated,
3379                    "substrate={populated:?}: query {query:?} classification drifted",
3380                );
3381            }
3382        }
3383    }
3384
3385    /// GATE-COMPUTE BASELINE — the workspace-baseline
3386    /// [`Classification::gate_compute`] shape carries
3387    /// `substrate: Compute`, so `has_substrate` returns `true` on
3388    /// [`SubstrateType::Compute`] and `false` on every other of the
3389    /// eight variants. Pins the composition of the substrate's
3390    /// baseline-constructor primitive with the fourth scalar-carrier
3391    /// presence probe — a regression that flipped
3392    /// `gate_compute().substrate` off `Compute` (or wired
3393    /// `has_substrate` to a fixed variant answer, or crossed the
3394    /// wires to `point_type`) fails here at ONE narrow site before
3395    /// drifting across every unadorned ephemeral env
3396    /// (`default_ephemeral_class`) and every downstream test fixture
3397    /// that keys assertions on the shape. Byte-symmetric with the
3398    /// peer `classification_gate_compute_has_point_type_gate_only`
3399    /// pin on the third scalar-carrier — the two co-tenants on the
3400    /// (required-parent × required-scalar-child) corner walk their
3401    /// own required axis independently.
3402    #[test]
3403    fn classification_gate_compute_has_substrate_compute_only() {
3404        let c = Classification::gate_compute();
3405        for kind in SubstrateType::ALL {
3406            let expected = kind == SubstrateType::Compute;
3407            assert_eq!(
3408                c.has_substrate(kind),
3409                expected,
3410                "gate_compute (substrate=Compute) must return {expected} for {kind:?}",
3411            );
3412        }
3413    }
3414
3415    /// TWO-AXIS INDEPENDENCE — the two co-tenants on the (required-
3416    /// parent × required-scalar-child) corner of the presence-probe
3417    /// algebra ([`Classification::has_point_type`] and
3418    /// [`Classification::has_substrate`]) probe distinct required
3419    /// scalar slots on the SAME [`Classification`] parent, so a
3420    /// carrier with `point_type: Fork` AND `substrate: Storage`
3421    /// answers `true` on both fine tags simultaneously and `false`
3422    /// on every off-diagonal probe of either axis. Pins the two
3423    /// probes' independence at ONE narrow site — a regression that
3424    /// collapsed either onto the other's field (a stray probe of
3425    /// `has_substrate` reading `self.point_type`, or of
3426    /// `has_point_type` reading `self.substrate`) would fail HERE
3427    /// before landing at any consumer. The audit `every Fork-topology
3428    /// Storage-plane point handles SIGHUP by Restart` composes this
3429    /// exact two-axis conjunction on the required scalars of the
3430    /// six-axis classification lattice.
3431    #[test]
3432    fn classification_has_point_type_and_has_substrate_are_independent() {
3433        let c = Classification {
3434            point_type: ConvergencePointType::Fork,
3435            substrate: SubstrateType::Storage,
3436            horizon: Horizon::default(),
3437            calm: CalmClassification::default(),
3438            data_classification: DataClassification::default(),
3439        };
3440        assert!(c.has_point_type(ConvergencePointType::Fork));
3441        assert!(c.has_substrate(SubstrateType::Storage));
3442        assert!(!c.has_point_type(ConvergencePointType::Gate));
3443        assert!(!c.has_substrate(SubstrateType::Compute));
3444        // Cross-wiring probe: `has_point_type(Storage-as-if-Point)` and
3445        // `has_substrate(Fork-as-if-Substrate)` cannot even typecheck
3446        // — the closed-set enums are disjoint types — but a stray
3447        // implementation reading the WRONG required field would flip
3448        // both diagonal answers off. The four asserts above pin the
3449        // independence at ONE narrow site.
3450    }
3451
3452    // ── scalar-carrier presence probe on Classification × CalmClassification ──
3453    //
3454    // Fail-before-pass-after granularity: [`Classification::has_calm`]
3455    // did not exist before this commit — every consumer of the
3456    // `(Classification, CalmClassification) -> bool` scalar-carrier
3457    // probe shape restated the `classification.calm == kind` equality
3458    // body at its own callsite. Post-lift the shape lives at ONE
3459    // substrate owner and every downstream (the `calm-<kind>`
3460    // require-tag family in `tatara-check`, future audit dispatchers
3461    // walking [`CalmClassification::ALL`], any future CRD-facing
3462    // closed-set discriminator on a defaulted scalar `ProcessSpec`
3463    // field such as `has_data_classification`) binds through the SAME
3464    // `has(kind)` shape the Option-slot (`Intent::has`, `Lifetime::has`),
3465    // slice-level (`ConditionSliceExt::has_kind`,
3466    // `DependsOnSliceExt::has_must_reach`,
3467    // `ComplianceBindingSliceExt::has_verification_phase`,
3468    // `ExportSpecSliceExt::has_{when,channel_kind,report_format,artifact_kind}`),
3469    // and prior scalar-carrier
3470    // (`SignalPolicy::has_sighup_strategy`,
3471    // `EncapsulatesSpec::has_mode`, `Classification::has_point_type`,
3472    // `Classification::has_substrate`) peers publish. FIRST occupant
3473    // on the (required-parent × defaulted-scalar-child) corner of the
3474    // presence-probe algebra — a fresh corner distinct from all four
3475    // prior scalar-carrier peers.
3476
3477    /// DIAGONAL — for every [`CalmClassification`] variant, a
3478    /// [`Classification`] whose `calm` field is set to that variant
3479    /// returns `true` from `has_calm` on that same variant AND
3480    /// `false` on every other variant. Sweep the
3481    /// [`CalmClassification::ALL`] × ALL cross so a regression that
3482    /// hard-coded the arm to a single variant (silently returning
3483    /// `true` on every populated classification regardless of query
3484    /// kind) or wired the equality to a fixed unrelated field (a
3485    /// stray probe on `classification.point_type` or
3486    /// `classification.substrate`) fails HERE at the substrate
3487    /// primitive before landing at the operator-facing checks.lisp
3488    /// surface.
3489    #[test]
3490    fn classification_has_calm_returns_true_iff_variant_matches() {
3491        for populated in CalmClassification::ALL {
3492            let c = Classification {
3493                point_type: ConvergencePointType::Gate,
3494                substrate: SubstrateType::Compute,
3495                horizon: Horizon::default(),
3496                calm: populated,
3497                data_classification: DataClassification::default(),
3498            };
3499            for query in CalmClassification::ALL {
3500                assert_eq!(
3501                    c.has_calm(query),
3502                    query == populated,
3503                    "calm={populated:?}: query {query:?} classification drifted",
3504                );
3505            }
3506        }
3507    }
3508
3509    /// GATE-COMPUTE BASELINE — the workspace-baseline
3510    /// [`Classification::gate_compute`] shape carries
3511    /// `calm: CalmClassification::default()` which is
3512    /// [`CalmClassification::Monotone`] via `#[default]`, so
3513    /// `has_calm` returns `true` on [`CalmClassification::Monotone`]
3514    /// and `false` on [`CalmClassification::NonMonotone`]. Pins the
3515    /// composition of the substrate's baseline-constructor primitive
3516    /// with the FIFTH scalar-carrier presence probe AND the sibling-
3517    /// default correspondence documented on [`Classification::gate_compute`]
3518    /// (which pins the three defaulted axes to the sibling closed-set
3519    /// defaults `HorizonKind::Bounded` / `CalmClassification::Monotone`
3520    /// / `DataClassification::Internal`) — a regression that flipped
3521    /// `gate_compute().calm` off `Monotone` (or promoted a different
3522    /// variant to `#[default]` on the closed set, or wired `has_calm`
3523    /// to a fixed variant answer, or crossed the wires to
3524    /// `point_type` / `substrate`) fails here at ONE narrow site
3525    /// before drifting across every unadorned ephemeral env
3526    /// (`default_ephemeral_class`) and every downstream test fixture
3527    /// that keys assertions on the shape. FIRST occupant on the
3528    /// (required-parent × defaulted-scalar-child) corner — locks the
3529    /// corner's characteristic "default-arm short-circuit" property
3530    /// at ONE narrow classifier site: a bare classification answers
3531    /// `true` on the default variant (distinct from the
3532    /// required-child corner peers, where a bare classification must
3533    /// name a variant deliberately to answer `true`).
3534    #[test]
3535    fn classification_gate_compute_has_calm_monotone_only() {
3536        let c = Classification::gate_compute();
3537        for kind in CalmClassification::ALL {
3538            let expected = kind == CalmClassification::Monotone;
3539            assert_eq!(
3540                c.has_calm(kind),
3541                expected,
3542                "gate_compute (calm=Monotone) must return {expected} for {kind:?}",
3543            );
3544        }
3545    }
3546
3547    /// THREE-AXIS INDEPENDENCE — the three co-tenants on the
3548    /// [`Classification`] parent
3549    /// ([`Classification::has_point_type`] +
3550    /// [`Classification::has_substrate`] on the (required-parent ×
3551    /// required-scalar-child) corner AND [`Classification::has_calm`]
3552    /// on the fresh (required-parent × defaulted-scalar-child)
3553    /// corner) probe distinct scalar slots on the SAME parent, so a
3554    /// carrier with `point_type: Fork` AND `substrate: Storage` AND
3555    /// `calm: NonMonotone` answers `true` on all three fine tags
3556    /// simultaneously and `false` on every off-diagonal probe of any
3557    /// axis. Pins the three probes' independence at ONE narrow site
3558    /// — a regression that collapsed any of the three onto another's
3559    /// field (a stray probe of `has_calm` reading `self.point_type`
3560    /// or `self.substrate`, or of either required-axis probe reading
3561    /// `self.calm`) would fail HERE before landing at any consumer.
3562    /// The audit `every Fork-topology Storage-plane NonMonotone-CALM
3563    /// point declares a Raft-guarded write path` composes this exact
3564    /// three-axis conjunction on the required + defaulted scalars of
3565    /// the six-axis classification lattice.
3566    #[test]
3567    fn classification_has_point_type_and_has_substrate_and_has_calm_are_independent() {
3568        let c = Classification {
3569            point_type: ConvergencePointType::Fork,
3570            substrate: SubstrateType::Storage,
3571            horizon: Horizon::default(),
3572            calm: CalmClassification::NonMonotone,
3573            data_classification: DataClassification::default(),
3574        };
3575        assert!(c.has_point_type(ConvergencePointType::Fork));
3576        assert!(c.has_substrate(SubstrateType::Storage));
3577        assert!(c.has_calm(CalmClassification::NonMonotone));
3578        assert!(!c.has_point_type(ConvergencePointType::Gate));
3579        assert!(!c.has_substrate(SubstrateType::Compute));
3580        assert!(!c.has_calm(CalmClassification::Monotone));
3581    }
3582
3583    // ── scalar-carrier presence probe on Classification × DataClassification ──
3584    //
3585    // Fail-before-pass-after granularity:
3586    // [`Classification::has_data_classification`] did not exist before
3587    // this commit — every consumer of the
3588    // `(Classification, DataClassification) -> bool` scalar-carrier
3589    // probe shape would have to restate the
3590    // `classification.data_classification == kind` equality body at
3591    // its own callsite. Post-lift the shape lives at ONE substrate
3592    // owner and every downstream (the `data-classification-<kind>`
3593    // require-tag family in `tatara-check`, future audit dispatchers
3594    // walking [`DataClassification::ALL`], any future CRD-facing
3595    // closed-set discriminator on a defaulted scalar `ProcessSpec`
3596    // field) binds through the SAME `has(kind)` shape the four prior
3597    // scalar-carrier peers on [`Classification`]
3598    // ([`Classification::has_point_type`],
3599    // [`Classification::has_substrate`],
3600    // [`Classification::has_calm`]) plus
3601    // [`crate::spec::SignalPolicy::has_sighup_strategy`] and
3602    // [`crate::encapsulates::EncapsulatesSpec::has_mode`] publish.
3603    // SECOND occupant on the (required-parent × defaulted-scalar-
3604    // child) corner of the presence-probe algebra after
3605    // [`Classification::has_calm`] opened it — pins the corner as a
3606    // proven-repeatable primitive shape rather than a single-example
3607    // curiosity and closes the four-scalar-carrier corner-coverage
3608    // contract on the six-axis classification lattice.
3609
3610    /// DIAGONAL — for every [`DataClassification`] variant, a
3611    /// [`Classification`] whose `data_classification` field is set to
3612    /// that variant returns `true` from `has_data_classification` on
3613    /// that same variant AND `false` on every other variant. Sweep
3614    /// the [`DataClassification::ALL`] × ALL cross so a regression
3615    /// that hard-coded the arm to a single variant (silently returning
3616    /// `true` on every populated classification regardless of query
3617    /// kind) or wired the equality to a fixed unrelated field (a
3618    /// stray probe on `classification.point_type` /
3619    /// `classification.substrate` / `classification.calm`) fails HERE
3620    /// at the substrate primitive before landing at the operator-
3621    /// facing checks.lisp surface.
3622    #[test]
3623    fn classification_has_data_classification_returns_true_iff_variant_matches() {
3624        for populated in DataClassification::ALL {
3625            let c = Classification {
3626                point_type: ConvergencePointType::Gate,
3627                substrate: SubstrateType::Compute,
3628                horizon: Horizon::default(),
3629                calm: CalmClassification::default(),
3630                data_classification: populated,
3631            };
3632            for query in DataClassification::ALL {
3633                assert_eq!(
3634                    c.has_data_classification(query),
3635                    query == populated,
3636                    "data_classification={populated:?}: query {query:?} classification drifted",
3637                );
3638            }
3639        }
3640    }
3641
3642    /// GATE-COMPUTE BASELINE — the workspace-baseline
3643    /// [`Classification::gate_compute`] shape carries
3644    /// `data_classification: DataClassification::default()` which is
3645    /// [`DataClassification::Internal`] via `#[default]`, so
3646    /// `has_data_classification` returns `true` on
3647    /// [`DataClassification::Internal`] and `false` on every other
3648    /// variant ([`DataClassification::Public`],
3649    /// [`DataClassification::Confidential`],
3650    /// [`DataClassification::Pii`], [`DataClassification::Phi`],
3651    /// [`DataClassification::Pci`]). Pins the composition of the
3652    /// substrate's baseline-constructor primitive with the SIXTH
3653    /// scalar-carrier presence probe AND the sibling-default
3654    /// correspondence documented on [`Classification::gate_compute`]
3655    /// (which pins the three defaulted axes to the sibling closed-set
3656    /// defaults `HorizonKind::Bounded` / `CalmClassification::Monotone`
3657    /// / `DataClassification::Internal`) — a regression that flipped
3658    /// `gate_compute().data_classification` off `Internal` (or
3659    /// promoted a different variant to `#[default]` on the closed
3660    /// set, or wired `has_data_classification` to a fixed variant
3661    /// answer, or crossed the wires to `point_type` / `substrate` /
3662    /// `calm`) fails here at ONE narrow site before drifting across
3663    /// every unadorned ephemeral env (`default_ephemeral_class`) and
3664    /// every downstream test fixture that keys assertions on the
3665    /// shape. SECOND occupant on the (required-parent × defaulted-
3666    /// scalar-child) corner — pins the corner's characteristic
3667    /// "default-arm short-circuit" property on its second occupant
3668    /// (peer to `classification_gate_compute_has_calm_monotone_only`
3669    /// which pins the same shape on the corner's first occupant).
3670    #[test]
3671    fn classification_gate_compute_has_data_classification_internal_only() {
3672        let c = Classification::gate_compute();
3673        for kind in DataClassification::ALL {
3674            let expected = kind == DataClassification::Internal;
3675            assert_eq!(
3676                c.has_data_classification(kind),
3677                expected,
3678                "gate_compute (data_classification=Internal) must return {expected} for {kind:?}",
3679            );
3680        }
3681    }
3682
3683    /// FOUR-AXIS INDEPENDENCE — the four scalar-carrier co-tenants
3684    /// on the [`Classification`] parent
3685    /// ([`Classification::has_point_type`] plus
3686    /// [`Classification::has_substrate`] on the (required-parent ×
3687    /// required-scalar-child) corner AND [`Classification::has_calm`]
3688    /// plus [`Classification::has_data_classification`] on the
3689    /// (required-parent × defaulted-scalar-child) corner) probe
3690    /// distinct scalar slots on the SAME parent, so a carrier with
3691    /// `point_type: Fork` AND `substrate: Storage` AND
3692    /// `calm: NonMonotone` AND `data_classification: Pii` answers
3693    /// `true` on all four fine tags simultaneously and `false` on
3694    /// every off-diagonal probe of any axis. Pins the four probes'
3695    /// independence at ONE narrow site — a regression that collapsed
3696    /// any of the four onto another's field (a stray probe of
3697    /// `has_data_classification` reading `self.point_type` /
3698    /// `self.substrate` / `self.calm`, or of any prior probe reading
3699    /// `self.data_classification`) would fail HERE before landing at
3700    /// any consumer. The audit `every Fork-topology Storage-plane
3701    /// NonMonotone-CALM Pii-classification point declares a
3702    /// Raft-guarded write path AND a downstream PII-scrub sink`
3703    /// composes this exact four-axis conjunction on the required +
3704    /// defaulted scalars of the six-axis classification lattice.
3705    /// Closes the four-scalar-carrier corner-coverage contract on
3706    /// [`Classification`] — its two required-scalar-child slots
3707    /// (`point_type`, `substrate`) AND its two defaulted-scalar-
3708    /// child slots (`calm`, `data_classification`) all publish
3709    /// independent presence probes through the same shape.
3710    #[test]
3711    fn classification_four_scalar_carrier_probes_are_independent() {
3712        let c = Classification {
3713            point_type: ConvergencePointType::Fork,
3714            substrate: SubstrateType::Storage,
3715            horizon: Horizon::default(),
3716            calm: CalmClassification::NonMonotone,
3717            data_classification: DataClassification::Pii,
3718        };
3719        assert!(c.has_point_type(ConvergencePointType::Fork));
3720        assert!(c.has_substrate(SubstrateType::Storage));
3721        assert!(c.has_calm(CalmClassification::NonMonotone));
3722        assert!(c.has_data_classification(DataClassification::Pii));
3723        assert!(!c.has_point_type(ConvergencePointType::Gate));
3724        assert!(!c.has_substrate(SubstrateType::Compute));
3725        assert!(!c.has_calm(CalmClassification::Monotone));
3726        assert!(!c.has_data_classification(DataClassification::Internal));
3727        assert!(!c.has_data_classification(DataClassification::Public));
3728        assert!(!c.has_data_classification(DataClassification::Phi));
3729    }
3730
3731    // ── nested-struct-scalar-carrier presence probe on Classification × HorizonKind ──
3732    //
3733    // Fail-before-pass-after granularity:
3734    // [`Classification::has_horizon_kind`] did not exist before this
3735    // commit — every consumer of the `(Classification, HorizonKind) ->
3736    // bool` two-hop `self.horizon.kind == kind` probe shape would have
3737    // to restate the nested-struct field walk at its own callsite.
3738    // Post-lift the shape lives at ONE substrate owner and every
3739    // downstream (the `horizon-<kind>` require-tag family in
3740    // `tatara-check`, future audit dispatchers walking
3741    // [`HorizonKind::ALL`], any future CRD-facing nested-struct-scalar
3742    // discriminator on `ProcessSpec`) binds through the SAME
3743    // `has(kind)` shape the four prior scalar-carrier peers on
3744    // [`Classification`] ([`Classification::has_point_type`],
3745    // [`Classification::has_substrate`], [`Classification::has_calm`],
3746    // [`Classification::has_data_classification`]) plus
3747    // [`crate::spec::SignalPolicy::has_sighup_strategy`] and
3748    // [`crate::encapsulates::EncapsulatesSpec::has_mode`] publish.
3749    // FIRST occupant on the (required-parent × nested-struct-scalar-
3750    // child) corner of the presence-probe algebra — a fresh corner
3751    // distinct from the four corner-property-exhaustive scalar-carrier
3752    // peers on [`Classification`] (whose bodies read a closed-set
3753    // discriminator directly off a scalar slot without an intermediate
3754    // struct hop).
3755
3756    /// DIAGONAL — for every [`HorizonKind`] variant, a
3757    /// [`Classification`] whose `horizon.kind` field is set to that
3758    /// variant returns `true` from `has_horizon_kind` on that same
3759    /// variant AND `false` on every other variant. Sweep the
3760    /// [`HorizonKind::ALL`] × ALL cross so a regression that
3761    /// hard-coded the arm to a single variant (silently returning
3762    /// `true` on every populated classification regardless of query
3763    /// kind) or wired the equality to a fixed unrelated field (a
3764    /// stray probe on `classification.point_type` /
3765    /// `classification.substrate` / `classification.calm` /
3766    /// `classification.data_classification`, or a direct probe on the
3767    /// nested [`Horizon`] struct that ignored the discriminator arm)
3768    /// fails HERE at the substrate primitive before landing at the
3769    /// operator-facing checks.lisp surface. The nested-struct hop
3770    /// distinguishes this corner from the four scalar-carrier peers:
3771    /// the probe walks `self.horizon.kind` not `self.<field>`, so a
3772    /// regression that mis-routed the field walk (a stray
3773    /// `self.horizon == kind` that could not typecheck, or a stray
3774    /// `self.horizon.direction == kind` that would trip a different
3775    /// closed-set discriminator) fails at the compiler before the
3776    /// runtime diagonal even runs.
3777    #[test]
3778    fn classification_has_horizon_kind_returns_true_iff_variant_matches() {
3779        for populated in HorizonKind::ALL {
3780            let c = Classification {
3781                point_type: ConvergencePointType::Gate,
3782                substrate: SubstrateType::Compute,
3783                horizon: Horizon {
3784                    kind: populated,
3785                    ..Horizon::default()
3786                },
3787                calm: CalmClassification::default(),
3788                data_classification: DataClassification::default(),
3789            };
3790            for query in HorizonKind::ALL {
3791                assert_eq!(
3792                    c.has_horizon_kind(query),
3793                    query == populated,
3794                    "horizon.kind={populated:?}: query {query:?} classification drifted",
3795                );
3796            }
3797        }
3798    }
3799
3800    /// GATE-COMPUTE BASELINE — the workspace-baseline
3801    /// [`Classification::gate_compute`] shape carries
3802    /// `horizon: Horizon::default()` whose `kind` field defaults to
3803    /// [`HorizonKind::Bounded`] via `#[default]`, so `has_horizon_kind`
3804    /// returns `true` on [`HorizonKind::Bounded`] and `false` on
3805    /// [`HorizonKind::Asymptotic`]. Pins the composition of the
3806    /// substrate's baseline-constructor primitive with the SEVENTH
3807    /// presence-probe peer AND the sibling-default correspondence
3808    /// documented on [`Classification::gate_compute`] (which pins the
3809    /// three defaulted axes to the sibling closed-set defaults
3810    /// `HorizonKind::Bounded` / `CalmClassification::Monotone` /
3811    /// `DataClassification::Internal`) — a regression that flipped
3812    /// `Horizon::default().kind` off `Bounded` (or promoted
3813    /// `Asymptotic` to `#[default]` on [`HorizonKind`], or wired
3814    /// `has_horizon_kind` to a fixed variant answer, or crossed the
3815    /// wires through the wrong nested struct) fails here at ONE
3816    /// narrow site before drifting across every unadorned ephemeral
3817    /// env (`default_ephemeral_class`) and every downstream test
3818    /// fixture that keys assertions on the shape. FIRST occupant on
3819    /// the (required-parent × nested-struct-scalar-child) corner —
3820    /// locks the corner's characteristic "default-arm short-circuit
3821    /// reaches through the nested struct's own default" property at
3822    /// ONE narrow site.
3823    #[test]
3824    fn classification_gate_compute_has_horizon_kind_bounded_only() {
3825        let c = Classification::gate_compute();
3826        for kind in HorizonKind::ALL {
3827            let expected = kind == HorizonKind::Bounded;
3828            assert_eq!(
3829                c.has_horizon_kind(kind),
3830                expected,
3831                "gate_compute (horizon.kind=Bounded) must return {expected} for {kind:?}",
3832            );
3833        }
3834    }
3835
3836    /// FIVE-AXIS INDEPENDENCE — the FIVE presence-probe co-tenants on
3837    /// the [`Classification`] parent
3838    /// ([`Classification::has_point_type`] plus
3839    /// [`Classification::has_substrate`] on the (required-parent ×
3840    /// required-scalar-child) corner AND
3841    /// [`Classification::has_calm`] plus
3842    /// [`Classification::has_data_classification`] on the (required-
3843    /// parent × defaulted-scalar-child) corner AND
3844    /// [`Classification::has_horizon_kind`] on the fresh (required-
3845    /// parent × nested-struct-scalar-child) corner) probe distinct
3846    /// slots on the SAME parent, so a carrier with `point_type: Fork`
3847    /// AND `substrate: Storage` AND `calm: NonMonotone` AND
3848    /// `data_classification: Pii` AND `horizon.kind: Asymptotic`
3849    /// answers `true` on all five fine tags simultaneously and
3850    /// `false` on every off-diagonal probe of any axis. Pins the five
3851    /// probes' independence at ONE narrow site — a regression that
3852    /// collapsed any of the five onto another's field (a stray probe
3853    /// of `has_horizon_kind` reading `self.point_type` /
3854    /// `self.substrate` / `self.calm` / `self.data_classification`,
3855    /// or of any prior probe reading through `self.horizon.kind`)
3856    /// would fail HERE before landing at any consumer. The audit
3857    /// `every Fork-topology Storage-plane NonMonotone-CALM
3858    /// Pii-classification Asymptotic-horizon point declares a
3859    /// Raft-guarded write path AND a downstream PII-scrub sink AND a
3860    /// rate-window healthy-threshold metric` composes this exact
3861    /// five-axis conjunction on the five classification-axis
3862    /// discriminators of the six-axis classification lattice — opens
3863    /// the five-way corner-coverage contract on [`Classification`],
3864    /// straddling THREE distinct corners of the (parent-shape ×
3865    /// child-shape) algebra (the required-child corner
3866    /// `has_point_type` + `has_substrate` share, the defaulted-child
3867    /// corner `has_calm` + `has_data_classification` share, and the
3868    /// nested-struct-child corner `has_horizon_kind` opens).
3869    #[test]
3870    fn classification_five_presence_probes_are_independent() {
3871        let c = Classification {
3872            point_type: ConvergencePointType::Fork,
3873            substrate: SubstrateType::Storage,
3874            horizon: Horizon {
3875                kind: HorizonKind::Asymptotic,
3876                ..Horizon::default()
3877            },
3878            calm: CalmClassification::NonMonotone,
3879            data_classification: DataClassification::Pii,
3880        };
3881        assert!(c.has_point_type(ConvergencePointType::Fork));
3882        assert!(c.has_substrate(SubstrateType::Storage));
3883        assert!(c.has_calm(CalmClassification::NonMonotone));
3884        assert!(c.has_data_classification(DataClassification::Pii));
3885        assert!(c.has_horizon_kind(HorizonKind::Asymptotic));
3886        assert!(!c.has_point_type(ConvergencePointType::Gate));
3887        assert!(!c.has_substrate(SubstrateType::Compute));
3888        assert!(!c.has_calm(CalmClassification::Monotone));
3889        assert!(!c.has_data_classification(DataClassification::Internal));
3890        assert!(!c.has_horizon_kind(HorizonKind::Bounded));
3891    }
3892
3893    // ── nested-struct-Option-scalar-carrier presence probe on Classification × OptimizationDirection ──
3894    //
3895    // Fail-before-pass-after granularity:
3896    // [`Classification::has_optimization_direction`] did not exist
3897    // before this commit — every consumer of the
3898    // `(Classification, OptimizationDirection) -> bool` two-hop
3899    // `self.horizon.direction.unwrap_or_default() == kind` probe
3900    // shape would have to restate the nested-struct-Option field
3901    // walk at its own callsite. Post-lift the shape lives at ONE
3902    // substrate owner and every downstream (the
3903    // `optimization-direction-<kind>` require-tag family in
3904    // `tatara-check`, future audit dispatchers walking
3905    // [`OptimizationDirection::ALL`], any future CRD-facing nested-
3906    // struct-Option-scalar discriminator on `ProcessSpec`) binds
3907    // through the SAME `has(kind)` shape the six prior presence
3908    // probes on [`Classification`] plus its cousins on
3909    // [`crate::spec::SignalPolicy`] and
3910    // [`crate::encapsulates::EncapsulatesSpec`] publish. SECOND
3911    // occupant on the (required-parent × nested-struct-scalar-
3912    // child) corner of the presence-probe algebra — the FIRST
3913    // occupant [`Classification::has_horizon_kind`] read the nested
3914    // scalar `horizon.kind: HorizonKind` DIRECTLY; this probe adds
3915    // the `Option`-hop through `direction: Option<OptimizationDirection>`
3916    // via `Option::unwrap_or_default`, pinning the corner as a
3917    // proven-repeatable primitive shape rather than a single-example
3918    // curiosity.
3919
3920    /// DIAGONAL — for every [`OptimizationDirection`] variant, a
3921    /// [`Classification`] whose `horizon.direction` field is set to
3922    /// `Some(that variant)` returns `true` from
3923    /// `has_optimization_direction` on that same variant AND
3924    /// `false` on every other variant. Sweep the
3925    /// [`OptimizationDirection::ALL`] × ALL cross so a regression
3926    /// that hard-coded the arm to a single variant (silently
3927    /// returning `true` on every populated classification regardless
3928    /// of query kind) or wired the equality to a fixed unrelated
3929    /// field (a stray probe on `classification.point_type` /
3930    /// `classification.substrate` / `classification.calm` /
3931    /// `classification.data_classification` /
3932    /// `classification.horizon.kind`, or a direct probe on the
3933    /// nested [`Horizon`] struct that ignored the `direction` arm)
3934    /// fails HERE at the substrate primitive before landing at the
3935    /// operator-facing checks.lisp surface. The `Option`-hop
3936    /// distinguishes this method from the direct-nested-scalar
3937    /// peer [`Classification::has_horizon_kind`]: the probe walks
3938    /// `self.horizon.direction.unwrap_or_default()` not
3939    /// `self.horizon.kind`, so a regression that mis-routed the
3940    /// field walk (a stray `self.horizon.kind == kind` that could
3941    /// not typecheck, or a stray `self.horizon == kind` that also
3942    /// could not typecheck) fails at the compiler before the
3943    /// runtime diagonal even runs.
3944    #[test]
3945    fn classification_has_optimization_direction_returns_true_iff_variant_matches() {
3946        for populated in OptimizationDirection::ALL {
3947            let c = Classification {
3948                point_type: ConvergencePointType::Gate,
3949                substrate: SubstrateType::Compute,
3950                horizon: Horizon {
3951                    kind: HorizonKind::Asymptotic,
3952                    direction: Some(populated),
3953                    ..Horizon::default()
3954                },
3955                calm: CalmClassification::default(),
3956                data_classification: DataClassification::default(),
3957            };
3958            for query in OptimizationDirection::ALL {
3959                assert_eq!(
3960                    c.has_optimization_direction(query),
3961                    query == populated,
3962                    "horizon.direction=Some({populated:?}): query {query:?} classification drifted",
3963                );
3964            }
3965        }
3966    }
3967
3968    /// GATE-COMPUTE BASELINE — the workspace-baseline
3969    /// [`Classification::gate_compute`] shape carries
3970    /// `horizon: Horizon::default()` whose `direction` field defaults
3971    /// to `None`. Under [`Option::unwrap_or_default`] the probe
3972    /// answers as if the field were `OptimizationDirection::default()`
3973    /// = [`OptimizationDirection::Minimize`] via `#[default]`, so
3974    /// `has_optimization_direction` returns `true` on
3975    /// [`OptimizationDirection::Minimize`] and `false` on
3976    /// [`OptimizationDirection::Maximize`]. Pins the composition of
3977    /// the substrate's baseline-constructor primitive with the
3978    /// EIGHTH presence-probe peer AND the closed-set-default
3979    /// correspondence documented on [`OptimizationDirection`] —
3980    /// a regression that flipped `OptimizationDirection::default()`
3981    /// off `Minimize` (which would silently invert every unadorned
3982    /// `Asymptotic` Process's rate-window evaluator polarity), or
3983    /// wired `has_optimization_direction` to a fixed variant answer,
3984    /// or crossed the wires through the wrong nested struct or the
3985    /// wrong Option-slot, fails here at ONE narrow site before
3986    /// drifting across every unadorned ephemeral env
3987    /// (`default_ephemeral_class`) and every downstream test fixture
3988    /// that keys assertions on the shape. SECOND occupant on the
3989    /// (required-parent × nested-struct-scalar-child) corner —
3990    /// locks the corner's Option-hop default-arm short-circuit
3991    /// property at ONE narrow site (the Option `None` folds onto
3992    /// the closed set's `#[default]` via `unwrap_or_default`,
3993    /// mirroring the direct-nested-scalar's default-arm short-
3994    /// circuit through the nested struct's own default).
3995    #[test]
3996    fn classification_gate_compute_has_optimization_direction_minimize_only() {
3997        let c = Classification::gate_compute();
3998        for kind in OptimizationDirection::ALL {
3999            let expected = kind == OptimizationDirection::Minimize;
4000            assert_eq!(
4001                c.has_optimization_direction(kind),
4002                expected,
4003                "gate_compute (horizon.direction=None ⇒ default Minimize) must return {expected} for {kind:?}",
4004            );
4005        }
4006    }
4007
4008    /// SIX-AXIS INDEPENDENCE — the SIX presence-probe co-tenants on
4009    /// the [`Classification`] parent
4010    /// ([`Classification::has_point_type`] plus
4011    /// [`Classification::has_substrate`] on the (required-parent ×
4012    /// required-scalar-child) corner AND
4013    /// [`Classification::has_calm`] plus
4014    /// [`Classification::has_data_classification`] on the (required-
4015    /// parent × defaulted-scalar-child) corner AND
4016    /// [`Classification::has_horizon_kind`] plus
4017    /// [`Classification::has_optimization_direction`] on the
4018    /// (required-parent × nested-struct-scalar-child) corner) probe
4019    /// distinct slots on the SAME parent, so a carrier with
4020    /// `point_type: Fork` AND `substrate: Storage` AND
4021    /// `calm: NonMonotone` AND `data_classification: Pii` AND
4022    /// `horizon.kind: Asymptotic` AND
4023    /// `horizon.direction: Some(Maximize)` answers `true` on all six
4024    /// fine tags simultaneously and `false` on every off-diagonal
4025    /// probe of any axis. Pins the six probes' independence at ONE
4026    /// narrow site — a regression that collapsed any of the six
4027    /// onto another's field (a stray probe of
4028    /// `has_optimization_direction` reading `self.point_type` /
4029    /// `self.substrate` / `self.calm` /
4030    /// `self.data_classification` / `self.horizon.kind`, or of any
4031    /// prior probe reading through `self.horizon.direction`) would
4032    /// fail HERE before landing at any consumer. The audit
4033    /// `every Fork-topology Storage-plane NonMonotone-CALM
4034    /// Pii-classification Asymptotic-horizon Maximize-direction
4035    /// point declares a rate-window healthy-threshold metric and a
4036    /// throughput-oriented SLO` composes this exact six-axis
4037    /// conjunction on the six classification-axis discriminators of
4038    /// the six-axis classification lattice — populates the six-way
4039    /// corner-coverage contract on [`Classification`], now
4040    /// straddling THREE distinct corners of the (parent-shape ×
4041    /// child-shape) algebra with TWO co-tenants each on the
4042    /// nested-struct-child corner: direct-nested-scalar
4043    /// (`has_horizon_kind`) and Option-nested-scalar
4044    /// (`has_optimization_direction`).
4045    #[test]
4046    fn classification_six_presence_probes_are_independent() {
4047        let c = Classification {
4048            point_type: ConvergencePointType::Fork,
4049            substrate: SubstrateType::Storage,
4050            horizon: Horizon {
4051                kind: HorizonKind::Asymptotic,
4052                direction: Some(OptimizationDirection::Maximize),
4053                ..Horizon::default()
4054            },
4055            calm: CalmClassification::NonMonotone,
4056            data_classification: DataClassification::Pii,
4057        };
4058        assert!(c.has_point_type(ConvergencePointType::Fork));
4059        assert!(c.has_substrate(SubstrateType::Storage));
4060        assert!(c.has_calm(CalmClassification::NonMonotone));
4061        assert!(c.has_data_classification(DataClassification::Pii));
4062        assert!(c.has_horizon_kind(HorizonKind::Asymptotic));
4063        assert!(c.has_optimization_direction(OptimizationDirection::Maximize));
4064        assert!(!c.has_point_type(ConvergencePointType::Gate));
4065        assert!(!c.has_substrate(SubstrateType::Compute));
4066        assert!(!c.has_calm(CalmClassification::Monotone));
4067        assert!(!c.has_data_classification(DataClassification::Internal));
4068        assert!(!c.has_horizon_kind(HorizonKind::Bounded));
4069        assert!(!c.has_optimization_direction(OptimizationDirection::Minimize));
4070    }
4071}