Skip to main content

omena_cascade/
model.rs

1//! Public data model for cascade ordering, selector witnesses, and proof reports.
2//!
3//! These serializable types are the stable boundary consumed by query,
4//! transform, conformance, fuzz, and LSP surfaces. They intentionally expose
5//! evidence fields instead of opaque booleans so later passes can explain why a
6//! cascade-sensitive rewrite was accepted or blocked.
7
8use serde::{Deserialize, Serialize};
9use std::{
10    cmp::Ordering,
11    collections::{BTreeMap, BTreeSet},
12};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
15#[serde(rename_all = "camelCase")]
16pub enum CascadeLevel {
17    UserAgentNormal,
18    UserNormal,
19    AuthorNormal,
20    InlineNormal,
21    Animation,
22    AuthorImportant,
23    UserImportant,
24    UserAgentImportant,
25    Transition,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
29#[serde(rename_all = "camelCase")]
30pub struct LayerRank(pub i32);
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
33#[serde(rename_all = "camelCase")]
34pub struct Specificity {
35    pub ids: u32,
36    pub classes: u32,
37    pub elements: u32,
38}
39
40impl Specificity {
41    pub const ZERO: Self = Self {
42        ids: 0,
43        classes: 0,
44        elements: 0,
45    };
46
47    pub const fn new(ids: u32, classes: u32, elements: u32) -> Self {
48        Self {
49            ids,
50            classes,
51            elements,
52        }
53    }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
57#[serde(rename_all = "camelCase")]
58/// Whether a specificity estimate is complete enough for exact cascade ordering.
59pub enum SpecificityExactnessV0 {
60    /// Every selector component that contributes specificity was modeled.
61    Exact,
62    /// The numeric specificity is only a lower bound because some syntax was unmodeled.
63    Inexact,
64}
65
66impl Ord for Specificity {
67    fn cmp(&self, other: &Self) -> Ordering {
68        (self.ids, self.classes, self.elements).cmp(&(other.ids, other.classes, other.elements))
69    }
70}
71
72impl PartialOrd for Specificity {
73    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
74        Some(self.cmp(other))
75    }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
79#[serde(rename_all = "camelCase")]
80pub struct ModuleRank {
81    pub distance_priority: u32,
82    pub import_order_priority: u32,
83    pub file_order_priority: u32,
84}
85
86impl ModuleRank {
87    pub const ZERO: Self = Self {
88        distance_priority: 0,
89        import_order_priority: 0,
90        file_order_priority: 0,
91    };
92
93    pub const fn new(
94        distance_priority: u32,
95        import_order_priority: u32,
96        file_order_priority: u32,
97    ) -> Self {
98        Self {
99            distance_priority,
100            import_order_priority,
101            file_order_priority,
102        }
103    }
104}
105
106impl Ord for ModuleRank {
107    fn cmp(&self, other: &Self) -> Ordering {
108        (
109            self.distance_priority,
110            self.import_order_priority,
111            self.file_order_priority,
112        )
113            .cmp(&(
114                other.distance_priority,
115                other.import_order_priority,
116                other.file_order_priority,
117            ))
118    }
119}
120
121impl PartialOrd for ModuleRank {
122    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
123        Some(self.cmp(other))
124    }
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
128#[serde(rename_all = "camelCase")]
129pub struct CascadeKey {
130    pub level: CascadeLevel,
131    pub layer_rank: LayerRank,
132    pub scope_proximity: u32,
133    pub specificity: Specificity,
134    pub module_rank: ModuleRank,
135    pub source_order: u32,
136}
137
138impl CascadeKey {
139    pub const fn new(
140        level: CascadeLevel,
141        layer_rank: LayerRank,
142        scope_proximity: u32,
143        specificity: Specificity,
144        module_rank: ModuleRank,
145        source_order: u32,
146    ) -> Self {
147        Self {
148            level,
149            layer_rank,
150            scope_proximity,
151            specificity,
152            module_rank,
153            source_order,
154        }
155    }
156}
157
158impl Ord for CascadeKey {
159    fn cmp(&self, other: &Self) -> Ordering {
160        self.level
161            .cmp(&other.level)
162            .then_with(|| self.layer_rank.cmp(&other.layer_rank))
163            .then_with(|| other.scope_proximity.cmp(&self.scope_proximity))
164            .then_with(|| self.specificity.cmp(&other.specificity))
165            .then_with(|| self.source_order.cmp(&other.source_order))
166    }
167}
168
169impl PartialOrd for CascadeKey {
170    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
171        Some(self.cmp(other))
172    }
173}
174
175#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
176#[serde(rename_all = "camelCase")]
177pub struct CascadeDeclaration {
178    pub id: String,
179    pub property: String,
180    pub value: CascadeValue,
181    pub key: CascadeKey,
182    /// Trust boundary for using `key.specificity` to mint an exact winner.
183    pub specificity_exactness: SpecificityExactnessV0,
184}
185
186#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
187#[serde(rename_all = "camelCase")]
188pub struct CascadeProof {
189    pub declaration_id: String,
190    pub property: String,
191    pub level: CascadeLevel,
192    pub layer_rank: LayerRank,
193    pub scope_proximity: u32,
194    pub specificity: Specificity,
195    pub module_rank: ModuleRank,
196    pub source_order: u32,
197}
198
199impl CascadeProof {
200    pub fn from_declaration(declaration: &CascadeDeclaration) -> Self {
201        assert_eq!(
202            declaration.specificity_exactness,
203            SpecificityExactnessV0::Exact,
204            "cascade proofs require exact specificity"
205        );
206        Self {
207            declaration_id: declaration.id.clone(),
208            property: declaration.property.clone(),
209            level: declaration.key.level,
210            layer_rank: declaration.key.layer_rank,
211            scope_proximity: declaration.key.scope_proximity,
212            specificity: declaration.key.specificity,
213            module_rank: declaration.key.module_rank,
214            source_order: declaration.key.source_order,
215        }
216    }
217}
218
219#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
220#[serde(rename_all = "camelCase")]
221pub enum CascadeOutcome {
222    Definite {
223        winner: CascadeDeclaration,
224        proof: Box<CascadeProof>,
225        also_considered: Vec<CascadeDeclaration>,
226    },
227    RankedSet(Vec<CascadeDeclaration>),
228    Inherit,
229    Top,
230}
231
232#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
233#[serde(rename_all = "camelCase")]
234pub enum CascadeValue {
235    Literal(String),
236    Composite(Vec<CascadeValue>),
237    Var {
238        name: String,
239        fallback: Option<Box<CascadeValue>>,
240    },
241    Initial,
242    Inherit,
243    Indeterminate,
244    GuaranteedInvalid,
245    Unset,
246}
247
248#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
249#[serde(rename_all = "camelCase")]
250pub enum ComputedCascadeValueStatusV0 {
251    Resolved,
252    Inherited,
253    Initial,
254    Indeterminate,
255    InvalidAtComputedValueTime,
256}
257
258macro_rules! define_computed_cascade_indeterminate_reasons {
259    ($($variant:ident => $wire_name:literal),+ $(,)?) => {
260        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
261        #[serde(rename_all = "camelCase")]
262        pub enum ComputedCascadeIndeterminateReasonV0 {
263            $($variant),+
264        }
265
266        impl ComputedCascadeIndeterminateReasonV0 {
267            #[cfg(test)]
268            pub(crate) const ALL: &'static [Self] = &[$(Self::$variant),+];
269
270            pub const fn wire_name(self) -> &'static str {
271                match self {
272                    $(Self::$variant => $wire_name),+
273                }
274            }
275        }
276    };
277}
278
279define_computed_cascade_indeterminate_reasons! {
280    CascadeOutcomeIndeterminate => "cascadeOutcomeIndeterminate",
281    PropertyInheritanceMetadataUnavailable => "propertyInheritanceMetadataUnavailable",
282    PropertyInitialValueMetadataUnavailable => "propertyInitialValueMetadataUnavailable",
283    RegisteredPropertySyntaxIndeterminate => "registeredPropertySyntaxIndeterminate",
284    InheritedFromIndeterminateParent => "inheritedFromIndeterminateParent",
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
288#[serde(rename_all = "camelCase")]
289pub enum CascadeRegisteredValueVerdictV0 {
290    Matched,
291    Unmatched,
292    Unknown,
293}
294
295#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
296#[serde(rename_all = "camelCase")]
297pub struct CascadeRegisteredCustomPropertyV0 {
298    pub name: String,
299    pub inherits: bool,
300    pub initial_value: CascadeValue,
301    pub declaration_value_verdicts: BTreeMap<String, CascadeRegisteredValueVerdictV0>,
302}
303
304#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
305#[serde(rename_all = "camelCase")]
306pub struct CascadeComputedValueInputV0 {
307    pub property: String,
308    pub declarations: Vec<CascadeDeclaration>,
309    pub custom_property_env: CustomPropertyEnv,
310    pub parent_computed_value: Option<CascadeValue>,
311    #[serde(skip_serializing_if = "Option::is_none")]
312    pub registered_custom_property: Option<CascadeRegisteredCustomPropertyV0>,
313}
314
315#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
316#[serde(rename_all = "camelCase")]
317pub struct CascadeComputedValueResultV0 {
318    pub schema_version: &'static str,
319    pub product: &'static str,
320    pub property: String,
321    pub status: ComputedCascadeValueStatusV0,
322    pub value: CascadeValue,
323    pub winner_declaration_id: Option<String>,
324    pub inherited: bool,
325    pub used_initial_value: bool,
326    pub invalid_at_computed_value_time: bool,
327    #[serde(skip_serializing_if = "Option::is_none")]
328    pub indeterminate_reason: Option<ComputedCascadeIndeterminateReasonV0>,
329    pub derivation_steps: Vec<&'static str>,
330}
331
332#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
333#[serde(rename_all = "camelCase")]
334pub enum SelectorContextMatchKind {
335    NoMatch,
336    Global,
337    Root,
338    Exact,
339    ContainsSelector,
340    ApproximateSelector,
341}
342
343#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
344#[serde(rename_all = "camelCase")]
345pub struct SelectorContextWitness {
346    pub kind: SelectorContextMatchKind,
347    pub verdict: SelectorMatchVerdict,
348    pub matched: bool,
349    pub rank: usize,
350    pub declaration_selector: Option<String>,
351    pub reference_selector: Option<String>,
352}
353
354impl SelectorContextWitness {
355    pub fn no_match() -> Self {
356        Self {
357            kind: SelectorContextMatchKind::NoMatch,
358            verdict: SelectorMatchVerdict::No,
359            matched: false,
360            rank: 0,
361            declaration_selector: None,
362            reference_selector: None,
363        }
364    }
365}
366
367#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
368#[serde(rename_all = "camelCase")]
369pub struct ElementSignature {
370    pub tag: Option<String>,
371    pub id: Option<String>,
372    pub classes: BTreeSet<String>,
373    pub attributes: BTreeSet<String>,
374    pub pseudo_states: BTreeSet<String>,
375    pub classes_are_exact: bool,
376    pub attributes_are_exact: bool,
377    pub pseudo_states_are_exact: bool,
378    pub tag_is_exact: bool,
379    pub id_is_exact: bool,
380}
381
382#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
383#[serde(rename_all = "camelCase")]
384pub struct ElementIdentityV0 {
385    pub source_path: String,
386    pub byte_start: usize,
387    pub byte_end: usize,
388}
389
390#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
391#[serde(rename_all = "camelCase")]
392pub struct ElementSignatureWithParentsV0 {
393    pub identity: ElementIdentityV0,
394    pub signature: ElementSignature,
395    pub parent_chain: Vec<ElementIdentityV0>,
396    pub parent_chain_complete: bool,
397}
398
399#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
400#[serde(rename_all = "camelCase")]
401pub enum ElementParentChainStatusV0 {
402    Complete,
403    MissingSource,
404    MissingElement,
405    AmbiguousParent,
406    Cycle,
407}
408
409#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
410#[serde(rename_all = "camelCase")]
411pub struct ElementParentChainV0 {
412    pub target: ElementIdentityV0,
413    pub ancestors: Vec<ElementIdentityV0>,
414    pub status: ElementParentChainStatusV0,
415}
416
417#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
418#[serde(rename_all = "camelCase")]
419pub enum ScopeProximityStatusV0 {
420    Known,
421    IncompleteParentChain,
422    MissingElementSignature,
423    UnsupportedRootSelector,
424    NoMatchingRoot,
425}
426
427#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
428#[serde(rename_all = "camelCase")]
429pub struct ScopeProximityV0 {
430    pub status: ScopeProximityStatusV0,
431    pub distance: Option<u32>,
432    pub matched_root: Option<ElementIdentityV0>,
433    pub examined_element_count: usize,
434}
435
436impl ScopeProximityV0 {
437    pub const fn unknown(status: ScopeProximityStatusV0) -> Self {
438        Self {
439            status,
440            distance: None,
441            matched_root: None,
442            examined_element_count: 0,
443        }
444    }
445}
446
447impl ElementParentChainV0 {
448    pub fn is_complete(&self) -> bool {
449        self.status == ElementParentChainStatusV0::Complete
450    }
451}
452
453impl ElementSignature {
454    pub fn concrete(
455        tag: Option<impl Into<String>>,
456        id: Option<impl Into<String>>,
457        classes: impl IntoIterator<Item = impl Into<String>>,
458    ) -> Self {
459        Self {
460            tag: tag.map(Into::into),
461            id: id.map(Into::into),
462            classes: classes.into_iter().map(Into::into).collect(),
463            attributes: BTreeSet::new(),
464            pseudo_states: BTreeSet::new(),
465            classes_are_exact: true,
466            attributes_are_exact: true,
467            pseudo_states_are_exact: true,
468            tag_is_exact: true,
469            id_is_exact: true,
470        }
471    }
472
473    pub fn at_least_classes(classes: impl IntoIterator<Item = impl Into<String>>) -> Self {
474        Self {
475            classes_are_exact: false,
476            ..Self::concrete(None::<String>, None::<String>, classes)
477        }
478    }
479}
480
481#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
482#[serde(rename_all = "camelCase")]
483pub struct SelectorSignature {
484    pub selector: String,
485    pub required_tag: Option<String>,
486    pub required_id: Option<String>,
487    pub required_classes: BTreeSet<String>,
488    pub required_attributes: BTreeSet<String>,
489    pub required_pseudo_states: BTreeSet<String>,
490    pub specificity: Specificity,
491    pub specificity_exactness: SpecificityExactnessV0,
492}
493
494#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
495#[serde(rename_all = "camelCase")]
496pub enum SelectorMatchVerdict {
497    No,
498    Maybe,
499    Yes,
500}
501
502#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
503#[serde(rename_all = "camelCase")]
504pub enum SelectorMatchReason {
505    Universal,
506    SimpleCompound,
507    SelectorList,
508    MissingTag,
509    MissingId,
510    MissingClass,
511    MissingAttribute,
512    MissingPseudoState,
513    UnsupportedSelector,
514}
515
516#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
517#[serde(rename_all = "camelCase")]
518pub struct SelectorMatchWitness {
519    pub selector: String,
520    pub matched_branch: Option<String>,
521    pub verdict: SelectorMatchVerdict,
522    pub reason: SelectorMatchReason,
523    pub specificity: Specificity,
524    pub missing_tag: Option<String>,
525    pub missing_id: Option<String>,
526    pub missing_classes: BTreeSet<String>,
527    pub missing_attributes: BTreeSet<String>,
528    pub missing_pseudo_states: BTreeSet<String>,
529    pub unsupported_branches: Vec<String>,
530}
531
532impl SelectorMatchWitness {
533    pub(crate) fn unsupported(selector: &str) -> Self {
534        Self {
535            selector: selector.to_string(),
536            matched_branch: Some(selector.to_string()),
537            verdict: SelectorMatchVerdict::Maybe,
538            reason: SelectorMatchReason::UnsupportedSelector,
539            specificity: Specificity::ZERO,
540            missing_tag: None,
541            missing_id: None,
542            missing_classes: BTreeSet::new(),
543            missing_attributes: BTreeSet::new(),
544            missing_pseudo_states: BTreeSet::new(),
545            unsupported_branches: vec![selector.to_string()],
546        }
547    }
548}
549
550#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
551#[serde(rename_all = "camelCase")]
552pub struct CascadeBoundarySummary {
553    pub product: &'static str,
554    pub ordering_model: &'static str,
555    pub substitution_model: &'static str,
556    pub least_fixed_point_proof_model: &'static str,
557    pub ready_surfaces: Vec<&'static str>,
558    pub not_ready_surfaces: Vec<&'static str>,
559}
560
561#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
562#[serde(rename_all = "camelCase")]
563pub struct CascadeConformanceSeedCase {
564    pub name: String,
565    pub property: &'static str,
566    pub declarations: Vec<CascadeDeclaration>,
567    pub expected_outcome: &'static str,
568    pub expected_winner_id: Option<String>,
569}
570
571#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
572#[serde(rename_all = "camelCase")]
573pub struct CascadeConformanceSeedResult {
574    pub name: String,
575    pub passed: bool,
576    pub expected_outcome: &'static str,
577    pub actual_outcome: &'static str,
578    pub expected_winner_id: Option<String>,
579    pub actual_winner_id: Option<String>,
580}
581
582#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
583#[serde(rename_all = "camelCase")]
584pub struct CascadeConformanceSeedReport {
585    pub schema_version: &'static str,
586    pub product: &'static str,
587    pub case_count: usize,
588    pub passed_count: usize,
589    pub failed_count: usize,
590    pub results: Vec<CascadeConformanceSeedResult>,
591}
592
593#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
594#[serde(rename_all = "camelCase")]
595pub struct CascadeEvaluationFuzzCaseV0 {
596    pub seed: u64,
597    pub declaration_count: usize,
598}
599
600#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
601#[serde(rename_all = "camelCase")]
602pub struct CascadeEvaluationFuzzResultV0 {
603    pub seed: u64,
604    pub declaration_count: usize,
605    pub actual_winner_id: Option<String>,
606    pub expected_winner_id: Option<String>,
607    pub ranked_count: usize,
608    pub passed: bool,
609}
610
611#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
612#[serde(rename_all = "camelCase")]
613pub struct VarSubstitutionFuzzCaseV0 {
614    pub seed: u64,
615    pub chain_len: usize,
616    pub cycle: bool,
617}
618
619#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
620#[serde(rename_all = "camelCase")]
621pub struct VarSubstitutionFuzzResultV0 {
622    pub seed: u64,
623    pub chain_len: usize,
624    pub cycle: bool,
625    pub result: CascadeValue,
626    pub expected: CascadeValue,
627    pub passed: bool,
628}
629
630#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
631#[serde(rename_all = "camelCase")]
632pub struct CustomPropertyLeastFixedPointSummaryV0 {
633    pub schema_version: &'static str,
634    pub product: &'static str,
635    pub input_count: usize,
636    pub resolved_count: usize,
637    pub guaranteed_invalid_count: usize,
638    pub iteration_count: usize,
639    pub iteration_bound: usize,
640    pub reached_fixed_point: bool,
641    pub monotone_witness_valid: bool,
642    pub proof: CustomPropertyLeastFixedPointProofV0,
643    pub iteration_trace: Vec<CustomPropertyLeastFixedPointIterationV0>,
644    pub entries: Vec<CustomPropertyLeastFixedPointEntryV0>,
645    pub ready_surfaces: Vec<&'static str>,
646}
647
648#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
649#[serde(rename_all = "camelCase")]
650pub struct CustomPropertyLeastFixedPointProofV0 {
651    pub finite_domain: &'static str,
652    pub transfer_function: &'static str,
653    pub monotone_witness: &'static str,
654    pub iteration_bound_formula: &'static str,
655    pub cycle_policy: &'static str,
656    pub proof_obligations: Vec<&'static str>,
657}
658
659#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
660#[serde(rename_all = "camelCase")]
661pub struct CustomPropertyLeastFixedPointIterationV0 {
662    pub iteration: usize,
663    pub changed_count: usize,
664    pub settled_count: usize,
665    pub guaranteed_invalid_count: usize,
666}
667
668#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
669#[serde(rename_all = "camelCase")]
670pub struct CustomPropertyLeastFixedPointEntryV0 {
671    pub name: String,
672    pub input: CascadeValue,
673    pub resolved: CascadeValue,
674    pub changed: bool,
675    pub guaranteed_invalid: bool,
676}
677
678#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
679#[serde(rename_all = "camelCase")]
680pub struct CascadeFuzzSeedReportV0 {
681    pub schema_version: &'static str,
682    pub product: &'static str,
683    pub case_count: usize,
684    pub passed_count: usize,
685    pub failed_count: usize,
686    pub cascade_results: Vec<CascadeEvaluationFuzzResultV0>,
687    pub var_results: Vec<VarSubstitutionFuzzResultV0>,
688}
689
690#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
691#[serde(rename_all = "camelCase")]
692pub struct BoxLonghandInputV0 {
693    pub property: String,
694    pub value: String,
695    pub important: bool,
696    pub source_order: u32,
697}
698
699pub type LonghandMergeInputV0 = BoxLonghandInputV0;
700
701#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
702#[serde(rename_all = "camelCase")]
703pub struct ShorthandCombinationProofV0 {
704    pub schema_version: &'static str,
705    pub product: &'static str,
706    pub shorthand_property: String,
707    pub accepted: bool,
708    pub blocked_reason: Option<&'static str>,
709    pub ordered_longhand_properties: Vec<String>,
710    pub provenance_preserved: bool,
711    pub cascade_safe_witness: String,
712}
713
714pub type LonghandMergeProofV0 = ShorthandCombinationProofV0;
715
716#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
717#[serde(default, rename_all = "camelCase")]
718pub struct SupportsTargetCapabilityV0 {
719    pub supports_light_dark: bool,
720    pub supports_color_mix: bool,
721    pub supports_oklch_oklab: bool,
722    pub supports_color_function: bool,
723    pub supports_relative_color: bool,
724    pub supports_logical_properties: bool,
725    pub supports_css_nesting: bool,
726    pub supports_css_scope: bool,
727    pub supports_cascade_layers: bool,
728}
729
730impl SupportsTargetCapabilityV0 {
731    pub const fn all_supported() -> Self {
732        Self {
733            supports_light_dark: true,
734            supports_color_mix: true,
735            supports_oklch_oklab: true,
736            supports_color_function: true,
737            supports_relative_color: true,
738            supports_logical_properties: true,
739            supports_css_nesting: true,
740            supports_css_scope: true,
741            supports_cascade_layers: true,
742        }
743    }
744
745    pub const fn none_supported() -> Self {
746        Self {
747            supports_light_dark: false,
748            supports_color_mix: false,
749            supports_oklch_oklab: false,
750            supports_color_function: false,
751            supports_relative_color: false,
752            supports_logical_properties: false,
753            supports_css_nesting: false,
754            supports_css_scope: false,
755            supports_cascade_layers: false,
756        }
757    }
758}
759
760impl Default for SupportsTargetCapabilityV0 {
761    fn default() -> Self {
762        Self::none_supported()
763    }
764}
765
766#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
767#[serde(rename_all = "camelCase")]
768pub enum StaticSupportsAssumptionV0 {
769    ModernBrowser,
770    TargetCapability(SupportsTargetCapabilityV0),
771}
772
773#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
774#[serde(rename_all = "camelCase")]
775pub enum StaticSupportsEvalVerdictV0 {
776    AlwaysTrue,
777    AlwaysFalse,
778    Unknown,
779}
780
781#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
782#[serde(rename_all = "camelCase")]
783pub struct StaticSupportsEvalWitnessV0 {
784    pub schema_version: &'static str,
785    pub product: &'static str,
786    pub condition: String,
787    pub assumption: StaticSupportsAssumptionV0,
788    pub verdict: StaticSupportsEvalVerdictV0,
789    pub reason: &'static str,
790    pub provenance_preserved: bool,
791}
792
793#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
794#[serde(rename_all = "camelCase")]
795pub struct ScopeFlattenInputV0 {
796    pub root_selector: String,
797    pub limit_selector: Option<String>,
798    pub scoped_rule_count: usize,
799    pub peer_scope_count: usize,
800    pub competing_unscoped_rule_count: usize,
801    pub inside_layer: bool,
802}
803
804#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
805#[serde(rename_all = "camelCase")]
806pub struct ScopeFlattenProofV0 {
807    pub schema_version: &'static str,
808    pub product: &'static str,
809    pub accepted: bool,
810    pub blocked_reason: Option<&'static str>,
811    pub root_selector: String,
812    pub provenance_preserved: bool,
813    pub cascade_safe_witness: String,
814}
815
816#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
817#[serde(rename_all = "camelCase")]
818pub struct LayerFlattenInputV0 {
819    pub layer_name: Option<String>,
820    pub layer_rule_count: usize,
821    pub peer_layer_count: usize,
822    pub unlayered_rule_count: usize,
823    pub important_declaration_count: usize,
824    pub closed_bundle: bool,
825}
826
827#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
828#[serde(rename_all = "camelCase")]
829pub struct LayerFlattenProofV0 {
830    pub schema_version: &'static str,
831    pub product: &'static str,
832    pub accepted: bool,
833    pub blocked_reason: Option<&'static str>,
834    pub layer_name: Option<String>,
835    pub provenance_preserved: bool,
836    pub cascade_safe_witness: String,
837}
838
839#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
840#[serde(tag = "witnessKind", content = "witness", rename_all = "camelCase")]
841pub enum ModalCheckWitnessSourceV0 {
842    ShorthandCombination(ShorthandCombinationProofV0),
843    StaticSupportsEval(StaticSupportsEvalWitnessV0),
844    ScopeFlatten(ScopeFlattenProofV0),
845    LayerFlatten(LayerFlattenProofV0),
846}
847
848#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
849#[serde(rename_all = "camelCase")]
850/// V0 freeze-candidate witness aggregation over existing cascade proof outputs.
851///
852/// This is a staged strict-superset surface for release evidence. It does not
853/// claim a completed modal theorem, paper-grade proof system, or Cargo 1.0 API.
854pub struct ModalCheckWitnessV0 {
855    pub schema_version: &'static str,
856    pub product: &'static str,
857    pub modal_family: &'static str,
858    pub substrate: &'static str,
859    pub obligation_count: usize,
860    pub accepted_count: usize,
861    pub blocked_count: usize,
862    pub all_provenance_preserved: bool,
863    pub source_products: Vec<&'static str>,
864    pub witnesses: Vec<ModalCheckWitnessSourceV0>,
865}
866
867#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
868#[serde(rename_all = "camelCase")]
869pub struct CascadeMarginSchemaV0 {
870    pub schema_version: &'static str,
871    pub product: &'static str,
872    pub margin_kind: &'static str,
873    pub axis_order: Vec<&'static str>,
874    pub calibration_stage: &'static str,
875    pub public_safety_claim_ready: bool,
876}
877
878#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
879#[serde(rename_all = "camelCase")]
880pub struct CascadeMarginV0 {
881    pub schema_version: &'static str,
882    pub product: &'static str,
883    pub margin_kind: &'static str,
884    pub winner_declaration_id: String,
885    pub challenger_declaration_id: Option<String>,
886    pub dominant_axis: &'static str,
887    pub signed_distance: i64,
888    pub winner_key: CascadeKey,
889    pub challenger_key: Option<CascadeKey>,
890    pub calibration_stage: &'static str,
891    pub public_safety_claim_ready: bool,
892}
893
894pub type CustomPropertyEnv = BTreeMap<String, CascadeValue>;