Skip to main content

omena_transform_passes/runtime/
executor.rs

1//! Transform pass execution over source text and explicit workspace context.
2//!
3//! The executor is the mutation boundary for ordered transform plans. It applies
4//! registered pass kinds, records provenance outcomes, and preserves semantic
5//! removal evidence for downstream query and consumer surfaces.
6
7use std::collections::BTreeSet;
8
9use omena_abstract_value::FactPrecision;
10use omena_cascade::StaticSupportsAssumptionV0;
11use omena_cascade_proof::{
12    CanonicalRewriteAssumptionsV0, DischargeLedgerLookupStatusV0,
13    REWRITE_CERTIFICATE_SCHEMA_VERSION_V0, REWRITE_RULE_CATALOG_SCHEMA_VERSION_V0,
14    RewriteCertificateEnvelopeV0, RewriteCertificateV0, RewriteOperatorV0, RewritePatternV0,
15    RewriteRuleCatalogV0, RewriteRuleV0, RewriteSideConditionKindV0, RewriteSubstitutionEntryV0,
16    RewriteTermV0, SideConditionCertV0, TokenOwnershipCertEntryV0,
17    TokenOwnershipSeparabilityCertV0, check_rewrite_certificate_v0,
18};
19use omena_evidence_graph::GuaranteeFamilyV0;
20use omena_parser::{
21    ClosedWorldBundleV0, ClosedWorldComposesScanStateV0, ClosedWorldInterfaceHashAvailabilityV0,
22    ClosedWorldModuleReachabilityEvidenceV0, ModuleInstanceKeyV0, ModuleQualifiedSymbolSetV0,
23    StyleDialect,
24};
25use omena_transform_cst::{
26    IrNodeKindV0, StableTransformIrNodeV0, TransformIrV0, TransformPassClassV0, TransformPassKind,
27    build_stable_transform_ir_from_source, lower_transform_ir_from_source,
28    strict_verification_build_profile, transform_pass_requires_closed_world_bundle,
29};
30
31use super::{
32    cascade_proof::{
33        collect_cascade_proof_obligations_for_ir_pass_input,
34        collect_cascade_proof_obligations_for_pass_input, summarize_cascade_proof_obligations,
35    },
36    outcome::{mutation_outcome, no_change_outcome, planned_only_outcome},
37    planner::{
38        default_transform_pass_registry, plan_transform_passes, transform_pass_kind_from_id,
39    },
40    provenance::{derive_transform_mutation_spans, provenance_derivation_forest_from_outcomes},
41    semantic_preservation::{
42        SemanticObservationProjectionV0, SemanticObservationScopeV0,
43        compare_semantic_observation_for_pass_with_scopes, semantic_preservation_applies,
44    },
45    winner_equality::{
46        TransformWinnerEqualityContextV0, TransformWinnerEqualityEvaluationV0,
47        driven_transform_axes, evaluate_transform_winner_equality, strict_required_winner_axes,
48    },
49};
50use crate::helpers::ir_transaction::{
51    reset_structural_ir_transaction_mutation_span_batches,
52    reset_structural_ir_transaction_telemetry, structural_ir_transaction_telemetry_snapshot,
53    take_structural_ir_transaction_mutation_span_batches,
54};
55use crate::model::{
56    CssModuleTokenOwnershipCensusV0, RollbackReceiptV0, RollbackScopeV0, TransformBlockedReasonV0,
57    TransformCascadeProofObligationV0, TransformClosedWorldAdmissionSummaryV0,
58    TransformCssModuleComposesResolutionV0, TransformDecision, TransformDesignTokenRouteV0,
59    TransformDischargeEvidenceV0, TransformDischargeLedgerTelemetryV0,
60    TransformEvaluationProfileV0, TransformExecutionContextV0, TransformExecutionPolicyV0,
61    TransformExecutionSummaryV0, TransformImportInlineV0, TransformModuleEvaluationNativeEditV0,
62    TransformModuleEvaluationV0, TransformModuleQualifiedExecutionErrorV0,
63    TransformModuleQualifiedShakeSummaryV0, TransformNoChangeReasonV0, TransformPassDispatchKindV0,
64    TransformPassExecutionOutcomeV0, TransformPassRegistryEntryV0, TransformPassRuntimeStatus,
65    TransformPreconditionV0, TransformProvenanceMutationSpanV0, TransformRejectionReasonV0,
66    TransformSemanticGuaranteeTierV0, TransformSemanticPreservationTelemetryV0,
67    TransformSemanticRemovalV0, TransformStrictPolicyReasonV0, TransformStrictPolicySummaryV0,
68    TransformStructuralDecisionClassV0, TransformStructuralDecisionPolicyV0,
69    TransformVendorPrefixPolicyV0, TransformWinnerEqualityAxisV0,
70    TransformWinnerEqualityObligationV0, TransformWinnerEqualityObservationV0,
71    transform_structural_decision_policy,
72};
73use crate::registry::{
74    add_css_vendor_prefixes, combine_css_shorthands, compress_css_colors,
75    compress_css_is_where_selectors, compress_css_numbers, css_module_composes_resolutions_for_ir,
76    dedupe_exact_css_rules_in_ir, evaluate_dead_media_branch_rules_in_ir,
77    evaluate_native_css_static_values_in_ir, evaluate_static_container_rules_in_ir,
78    evaluate_static_media_rules_in_ir, evaluate_static_supports_rules_in_ir,
79    flatten_css_layers_in_ir, flatten_css_scopes_in_ir, inline_css_imports_in_ir,
80    lower_css_color_function, lower_css_color_mix, lower_css_light_dark,
81    lower_css_logical_to_physical, lower_css_oklab_oklch, lower_relative_color,
82    merge_adjacent_same_block_css_selectors_in_ir, merge_adjacent_same_selector_css_rules_in_ir,
83    normalize_css_string_quotes, normalize_css_units, normalize_css_whitespace, reduce_css_calc,
84    remove_empty_css_rules_in_ir, remove_stale_css_vendor_prefixes,
85    resolve_css_module_composes_in_ir, resolve_static_css_modules_values,
86    rewrite_css_module_class_names_in_ir, route_design_token_values_in_ir, strip_css_comments,
87    strip_css_url_quotes, substitute_static_css_custom_properties,
88    tree_shake_css_class_rules_in_ir, tree_shake_css_custom_properties_in_ir,
89    tree_shake_css_keyframes_in_ir, tree_shake_css_modules_values_in_ir, unwrap_css_nesting_in_ir,
90};
91
92type TransformTextLocalRunnerV0 =
93    for<'a> fn(TransformTextLocalPassInputV0<'a>) -> TransformTextLocalPassOutputV0<'a>;
94
95#[derive(Clone, Copy)]
96struct TransformTextLocalPassHandlerV0 {
97    kind: TransformPassKind,
98    window_scope: TransformTextLocalWindowScopeV0,
99    execution_mode: TransformTextLocalExecutionModeV0,
100    detail: &'static str,
101    run: TransformTextLocalRunnerV0,
102}
103
104#[derive(Clone, Copy, PartialEq, Eq)]
105enum TransformTextLocalWindowScopeV0 {
106    DocumentTokenStream,
107    Selector,
108    DeclarationBlock,
109    DeclarationValue,
110}
111
112#[derive(Clone, Copy, PartialEq, Eq)]
113enum TransformTextLocalExecutionModeV0 {
114    FullDocument,
115    WindowBatch,
116}
117
118struct TransformPassDispatchResultV0 {
119    next_textual_css: Option<String>,
120    document_ir_updated: bool,
121    decision: TransformDecisionDraftV0,
122    css_module_evaluation: Option<TransformModuleEvaluationV0>,
123    css_import_inlines: Vec<TransformImportInlineV0>,
124    css_module_composes_exports: Vec<TransformCssModuleComposesResolutionV0>,
125    design_token_routes: Vec<TransformDesignTokenRouteV0>,
126    semantic_removals: Vec<TransformSemanticRemovalV0>,
127    provenance_mutation_spans: Option<Vec<TransformProvenanceMutationSpanV0>>,
128}
129
130enum TransformDecisionDraftV0 {
131    Applied {
132        outcome: TransformPassExecutionOutcomeV0,
133    },
134    NoChange {
135        reason: TransformNoChangeReasonV0,
136        outcome: TransformPassExecutionOutcomeV0,
137    },
138    Blocked {
139        reason: TransformBlockedReasonV0,
140        outcome: TransformPassExecutionOutcomeV0,
141    },
142    Rejected {
143        reason: TransformRejectionReasonV0,
144        outcome: TransformPassExecutionOutcomeV0,
145    },
146}
147
148#[derive(Clone, Copy)]
149enum TransformSemanticTrustRecordingV0 {
150    Record,
151    OmitForMeasurement,
152}
153
154impl TransformSemanticTrustRecordingV0 {
155    fn decision_tier(
156        self,
157        pass: Option<TransformPassKind>,
158        winner_tier: Option<TransformSemanticGuaranteeTierV0>,
159    ) -> Option<TransformSemanticGuaranteeTierV0> {
160        match self {
161            Self::Record => winner_tier.or_else(|| {
162                pass.filter(|pass| semantic_preservation_applies(*pass))
163                    .map(|_| TransformSemanticGuaranteeTierV0::L0Observed)
164            }),
165            Self::OmitForMeasurement => None,
166        }
167    }
168
169    fn records(self) -> bool {
170        matches!(self, Self::Record)
171    }
172}
173
174#[derive(Clone, Copy)]
175struct TransformExecutionRuntimePolicyV0<'a> {
176    verification: &'a TransformExecutionPolicyV0,
177    semantic_trust_recording: TransformSemanticTrustRecordingV0,
178    module_qualified_symbols: Option<&'a ModuleQualifiedSymbolSetV0>,
179    retained_class_names: &'a [String],
180    token_ownership_census: Option<&'a CssModuleTokenOwnershipCensusV0>,
181    token_ownership_module_instance: Option<&'a ModuleInstanceKeyV0>,
182}
183
184impl TransformDecisionDraftV0 {
185    fn applied_outcome(&self) -> Option<&TransformPassExecutionOutcomeV0> {
186        match self {
187            Self::Applied { outcome } => Some(outcome),
188            Self::NoChange { .. } | Self::Blocked { .. } | Self::Rejected { .. } => None,
189        }
190    }
191
192    #[cfg(test)]
193    fn compatibility_outcome(&self) -> &TransformPassExecutionOutcomeV0 {
194        match self {
195            Self::Applied { outcome }
196            | Self::NoChange { outcome, .. }
197            | Self::Blocked { outcome, .. }
198            | Self::Rejected { outcome, .. } => outcome,
199        }
200    }
201
202    fn finalize(
203        self,
204        input_content_signature: String,
205        preserved_output_signature: String,
206        discharge_evidence: Vec<TransformDischargeEvidenceV0>,
207        semantic_guarantee_tier: Option<TransformSemanticGuaranteeTierV0>,
208    ) -> TransformDecision {
209        match self {
210            Self::Applied { outcome } => TransformDecision::Applied {
211                rollback_receipt: RollbackReceiptV0 {
212                    pass_id: outcome.pass_id.to_string(),
213                    attempted_mutation_count: Some(outcome.mutation_count),
214                    input_content_signature,
215                    output_preserved_content_signature: None,
216                    restorable: RollbackScopeV0::CommittedIrrecoverable,
217                },
218                semantic_guarantee_tier,
219                discharge_evidence,
220                outcome,
221            },
222            Self::NoChange { reason, outcome } => TransformDecision::NoChange { reason, outcome },
223            Self::Blocked { reason, outcome } => TransformDecision::Blocked { reason, outcome },
224            Self::Rejected { reason, outcome } => {
225                assert_eq!(input_content_signature, preserved_output_signature);
226                TransformDecision::Rejected {
227                    rollback_receipt: RollbackReceiptV0 {
228                        pass_id: outcome.pass_id.to_string(),
229                        attempted_mutation_count: None,
230                        input_content_signature,
231                        output_preserved_content_signature: Some(preserved_output_signature),
232                        restorable: RollbackScopeV0::RejectPreservedInput,
233                    },
234                    reason,
235                    outcome,
236                }
237            }
238        }
239    }
240}
241
242#[derive(Clone, Copy)]
243enum TransformRuntimePassImplementationV0 {
244    TextLocal(TransformTextLocalPassHandlerV0),
245    Structural(TransformStructuralPassHandlerV0),
246    ModuleEvaluation(TransformPassKind),
247    Emission(TransformPassKind),
248}
249
250struct TransformRuntimePassEntryV0<'a> {
251    registry_entry: &'a TransformPassRegistryEntryV0,
252    implementation: TransformRuntimePassImplementationV0,
253}
254
255fn runtime_pass_entry_for_kind<'a>(
256    kind: TransformPassKind,
257    registry_entries: &'a [TransformPassRegistryEntryV0],
258) -> Option<TransformRuntimePassEntryV0<'a>> {
259    let registry_entry = registry_entries
260        .iter()
261        .find(|entry| entry.contract.kind == kind)?;
262    let implementation = runtime_pass_implementation_for_entry(registry_entry)?;
263    Some(TransformRuntimePassEntryV0 {
264        registry_entry,
265        implementation,
266    })
267}
268
269fn runtime_pass_implementation_for_entry(
270    entry: &TransformPassRegistryEntryV0,
271) -> Option<TransformRuntimePassImplementationV0> {
272    match entry.dispatch_kind {
273        TransformPassDispatchKindV0::TextLocalSliceRewrite => text_local_pass_handlers()
274            .iter()
275            .find(|handler| handler.kind == entry.contract.kind)
276            .copied()
277            .map(TransformRuntimePassImplementationV0::TextLocal),
278        TransformPassDispatchKindV0::StructuralIrTransaction => structural_pass_handlers()
279            .iter()
280            .find(|handler| handler.kind == entry.contract.kind)
281            .copied()
282            .map(TransformRuntimePassImplementationV0::Structural),
283        TransformPassDispatchKindV0::ModuleEvaluationHandler => Some(
284            TransformRuntimePassImplementationV0::ModuleEvaluation(entry.contract.kind),
285        ),
286        TransformPassDispatchKindV0::EmissionBoundary => Some(
287            TransformRuntimePassImplementationV0::Emission(entry.contract.kind),
288        ),
289    }
290}
291
292#[derive(Default)]
293struct TransformTextualBridgeSnapshotV0 {
294    css: Option<String>,
295}
296
297impl TransformTextualBridgeSnapshotV0 {
298    fn materialize_current_css<'a>(
299        &'a mut self,
300        document: &TransformExecutionDocumentV0,
301    ) -> &'a str {
302        self.css
303            .get_or_insert_with(|| document.current_css().to_string())
304            .as_str()
305    }
306}
307
308impl TransformPassDispatchResultV0 {
309    fn semantic_mutation_spans(&self) -> Vec<TransformProvenanceMutationSpanV0> {
310        let Some(outcome) = self.decision.applied_outcome() else {
311            return Vec::new();
312        };
313        if outcome.mutation_count == 0 {
314            return Vec::new();
315        }
316        self.provenance_mutation_spans.clone().unwrap_or_else(|| {
317            vec![TransformProvenanceMutationSpanV0 {
318                source_span_start: 0,
319                source_span_end: outcome.input_byte_len,
320                generated_span_start: 0,
321                generated_span_end: outcome.output_byte_len,
322                node_key: None,
323            }]
324        })
325    }
326
327    fn from_decision(next_textual_css: Option<String>, decision: TransformDecisionDraftV0) -> Self {
328        Self {
329            next_textual_css,
330            document_ir_updated: false,
331            decision,
332            css_module_evaluation: None,
333            css_import_inlines: Vec::new(),
334            css_module_composes_exports: Vec::new(),
335            design_token_routes: Vec::new(),
336            semantic_removals: Vec::new(),
337            provenance_mutation_spans: None,
338        }
339    }
340
341    fn textual_mutation(
342        pass_id: &'static str,
343        input_byte_len: usize,
344        next_css: String,
345        mutation_count: usize,
346        detail: &'static str,
347    ) -> Self {
348        let outcome = mutation_outcome(
349            pass_id,
350            input_byte_len,
351            next_css.len(),
352            mutation_count,
353            detail,
354        );
355        Self::from_mutation_outcome(Some(next_css), outcome)
356    }
357
358    fn ir_mutation(
359        pass_id: &'static str,
360        input_byte_len: usize,
361        output_byte_len: usize,
362        mutation_count: usize,
363        detail: &'static str,
364    ) -> Self {
365        let outcome = mutation_outcome(
366            pass_id,
367            input_byte_len,
368            output_byte_len,
369            mutation_count,
370            detail,
371        );
372        let mut result = Self::from_mutation_outcome(None, outcome);
373        result.document_ir_updated = true;
374        result
375    }
376
377    fn from_mutation_outcome(
378        next_textual_css: Option<String>,
379        outcome: TransformPassExecutionOutcomeV0,
380    ) -> Self {
381        let decision = match outcome.status {
382            TransformPassRuntimeStatus::Applied => TransformDecisionDraftV0::Applied { outcome },
383            TransformPassRuntimeStatus::NoChange => TransformDecisionDraftV0::NoChange {
384                reason: TransformNoChangeReasonV0::NoMutation,
385                outcome,
386            },
387            TransformPassRuntimeStatus::PlannedOnly => {
388                unreachable!("mutation outcomes cannot be planned-only")
389            }
390        };
391        Self::from_decision(next_textual_css, decision)
392    }
393
394    fn no_change(
395        pass_id: &'static str,
396        input_byte_len: usize,
397        reason: TransformNoChangeReasonV0,
398        detail: &'static str,
399    ) -> Self {
400        let outcome = no_change_outcome(pass_id, input_byte_len, input_byte_len, detail);
401        Self::from_decision(None, TransformDecisionDraftV0::NoChange { reason, outcome })
402    }
403
404    fn profile_only(
405        pass_id: &'static str,
406        input_byte_len: usize,
407        profile: TransformEvaluationProfileV0,
408        detail: &'static str,
409    ) -> Self {
410        let outcome = planned_only_outcome(pass_id, input_byte_len, input_byte_len, detail);
411        Self::from_decision(
412            None,
413            TransformDecisionDraftV0::NoChange {
414                reason: TransformNoChangeReasonV0::ProfileNotApplicable { profile },
415                outcome,
416            },
417        )
418    }
419
420    fn blocked(
421        pass_id: &'static str,
422        input_byte_len: usize,
423        reason: TransformBlockedReasonV0,
424        detail: &'static str,
425    ) -> Self {
426        let outcome = planned_only_outcome(pass_id, input_byte_len, input_byte_len, detail);
427        Self::from_decision(None, TransformDecisionDraftV0::Blocked { reason, outcome })
428    }
429
430    fn rejected(
431        pass_id: &'static str,
432        input_byte_len: usize,
433        reason: TransformRejectionReasonV0,
434        detail: &'static str,
435    ) -> Self {
436        let outcome = planned_only_outcome(pass_id, input_byte_len, input_byte_len, detail);
437        Self::from_decision(None, TransformDecisionDraftV0::Rejected { reason, outcome })
438    }
439}
440
441fn text_local_pass_handlers() -> &'static [TransformTextLocalPassHandlerV0] {
442    &TEXT_LOCAL_PASS_HANDLERS
443}
444
445fn text_local_execution_mode_for_kind(
446    kind: TransformPassKind,
447) -> Option<TransformTextLocalExecutionModeV0> {
448    text_local_pass_handlers()
449        .iter()
450        .find(|handler| handler.kind == kind)
451        .map(|handler| handler.execution_mode)
452}
453
454#[derive(Clone, Copy)]
455struct TransformTextLocalIrWindowV0<'a> {
456    source: &'a str,
457    source_span_start: usize,
458    source_span_end: usize,
459}
460
461impl<'a> TransformTextLocalIrWindowV0<'a> {
462    fn full_document(ir: &'a TransformIrV0) -> Vec<Self> {
463        vec![Self {
464            source: ir.source_text(),
465            source_span_start: 0,
466            source_span_end: ir.source_text().len(),
467        }]
468    }
469
470    fn for_scope(ir: &'a TransformIrV0, scope: TransformTextLocalWindowScopeV0) -> Vec<Self> {
471        match scope {
472            TransformTextLocalWindowScopeV0::DocumentTokenStream
473            | TransformTextLocalWindowScopeV0::Selector => Self::full_document(ir),
474            TransformTextLocalWindowScopeV0::DeclarationBlock
475            | TransformTextLocalWindowScopeV0::DeclarationValue => {
476                Self::root_rule_windows(ir).unwrap_or_else(|| Self::full_document(ir))
477            }
478        }
479    }
480
481    fn root_rule_windows(ir: &'a TransformIrV0) -> Option<Vec<Self>> {
482        let mut windows = ir
483            .root_nodes
484            .iter()
485            .filter_map(|node_id| {
486                let node = ir.nodes.get(node_id.index())?;
487                matches!(node.kind, IrNodeKindV0::StyleRule | IrNodeKindV0::AtRule).then(|| Self {
488                    source: ir.source_text(),
489                    source_span_start: node.source_span_start,
490                    source_span_end: node.source_span_end,
491                })
492            })
493            .collect::<Vec<_>>();
494        windows.sort_by_key(|window| (window.source_span_start, window.source_span_end));
495        (!windows.is_empty() && windows_are_non_overlapping(windows.as_slice())).then_some(windows)
496    }
497
498    fn source_text(self) -> &'a str {
499        self.source
500            .get(self.source_span_start..self.source_span_end)
501            .unwrap_or_default()
502    }
503}
504
505fn windows_are_non_overlapping(windows: &[TransformTextLocalIrWindowV0<'_>]) -> bool {
506    windows
507        .windows(2)
508        .all(|pair| pair[0].source_span_end <= pair[1].source_span_start)
509}
510
511struct TransformTextLocalPassInputV0<'a> {
512    source: &'a str,
513    source_windows: Vec<TransformTextLocalIrWindowV0<'a>>,
514    execution_mode: TransformTextLocalExecutionModeV0,
515    dialect: StyleDialect,
516    context: &'a TransformExecutionContextV0,
517}
518
519impl<'a> TransformTextLocalPassInputV0<'a> {
520    fn from_ir(
521        ir: &'a TransformIrV0,
522        scope: TransformTextLocalWindowScopeV0,
523        execution_mode: TransformTextLocalExecutionModeV0,
524        dialect: StyleDialect,
525        context: &'a TransformExecutionContextV0,
526    ) -> Self {
527        Self {
528            source: ir.source_text(),
529            source_windows: TransformTextLocalIrWindowV0::for_scope(ir, scope),
530            execution_mode,
531            dialect,
532            context,
533        }
534    }
535
536    fn rewrite_text_local(
537        self,
538        rewrite: impl FnMut(&str, StyleDialect, &TransformExecutionContextV0) -> (String, usize),
539    ) -> TransformTextLocalPassOutputV0<'a> {
540        match self.execution_mode {
541            TransformTextLocalExecutionModeV0::FullDocument => self.rewrite_full_document(rewrite),
542            TransformTextLocalExecutionModeV0::WindowBatch => self.rewrite_windows(rewrite),
543        }
544    }
545
546    fn rewrite_windows(
547        self,
548        mut rewrite: impl FnMut(&str, StyleDialect, &TransformExecutionContextV0) -> (String, usize),
549    ) -> TransformTextLocalPassOutputV0<'a> {
550        let mut rewrites = Vec::new();
551        let mut mutation_count = 0usize;
552        for window in &self.source_windows {
553            let window_source = window.source_text();
554            let (rewritten_css, window_mutation_count) =
555                rewrite(window_source, self.dialect, self.context);
556            mutation_count += window_mutation_count;
557            if window_mutation_count > 0 || rewritten_css != window_source {
558                rewrites.push(TransformTextLocalWindowRewriteV0 {
559                    source_span_start: window.source_span_start,
560                    source_span_end: window.source_span_end,
561                    rewritten_css,
562                });
563            }
564        }
565
566        TransformTextLocalPassOutputV0 {
567            input_byte_len: self.source.len(),
568            rewritten_css: apply_text_local_window_rewrites(self.source, rewrites.as_slice()),
569            provenance_mutation_spans: derive_text_local_window_mutation_spans(
570                self.source,
571                rewrites.as_slice(),
572            ),
573            mutation_count,
574            _lifetime: std::marker::PhantomData,
575        }
576    }
577
578    fn rewrite_full_document(
579        self,
580        mut rewrite: impl FnMut(&str, StyleDialect, &TransformExecutionContextV0) -> (String, usize),
581    ) -> TransformTextLocalPassOutputV0<'a> {
582        let (rewritten_css, mutation_count) = rewrite(self.source, self.dialect, self.context);
583        let provenance_mutation_spans =
584            derive_transform_mutation_spans(self.source, rewritten_css.as_str());
585        TransformTextLocalPassOutputV0 {
586            input_byte_len: self.source.len(),
587            rewritten_css,
588            provenance_mutation_spans,
589            mutation_count,
590            _lifetime: std::marker::PhantomData,
591        }
592    }
593}
594
595struct TransformTextLocalWindowRewriteV0 {
596    source_span_start: usize,
597    source_span_end: usize,
598    rewritten_css: String,
599}
600
601struct TransformTextLocalPassOutputV0<'a> {
602    input_byte_len: usize,
603    rewritten_css: String,
604    provenance_mutation_spans: Vec<TransformProvenanceMutationSpanV0>,
605    mutation_count: usize,
606    _lifetime: std::marker::PhantomData<&'a str>,
607}
608
609impl TransformTextLocalPassOutputV0<'_> {
610    fn input_byte_len(&self) -> usize {
611        self.input_byte_len
612    }
613
614    fn provenance_mutation_spans(&self) -> &[TransformProvenanceMutationSpanV0] {
615        self.provenance_mutation_spans.as_slice()
616    }
617
618    fn into_document_css(self) -> String {
619        self.rewritten_css
620    }
621}
622
623fn apply_text_local_window_rewrites(
624    source: &str,
625    rewrites: &[TransformTextLocalWindowRewriteV0],
626) -> String {
627    if rewrites.is_empty() {
628        return source.to_string();
629    }
630
631    let mut output = String::with_capacity(source.len());
632    let mut cursor = 0usize;
633    for rewrite in rewrites {
634        if rewrite.source_span_start < cursor {
635            continue;
636        }
637        if rewrite.source_span_start > cursor {
638            output.push_str(&source[cursor..rewrite.source_span_start]);
639        }
640        output.push_str(&rewrite.rewritten_css);
641        cursor = rewrite.source_span_end;
642    }
643    if cursor < source.len() {
644        output.push_str(&source[cursor..]);
645    }
646    output
647}
648
649fn derive_text_local_window_mutation_spans(
650    source: &str,
651    rewrites: &[TransformTextLocalWindowRewriteV0],
652) -> Vec<TransformProvenanceMutationSpanV0> {
653    let mut spans = Vec::new();
654    let mut generated_delta = 0isize;
655    for rewrite in rewrites {
656        let Some(source_window) = source.get(rewrite.source_span_start..rewrite.source_span_end)
657        else {
658            let rewritten_css = apply_text_local_window_rewrites(source, rewrites);
659            return derive_transform_mutation_spans(source, rewritten_css.as_str());
660        };
661        let Some(generated_window_start) =
662            add_signed_offset(rewrite.source_span_start, generated_delta)
663        else {
664            let rewritten_css = apply_text_local_window_rewrites(source, rewrites);
665            return derive_transform_mutation_spans(source, rewritten_css.as_str());
666        };
667        spans.extend(
668            derive_transform_mutation_spans(source_window, &rewrite.rewritten_css)
669                .into_iter()
670                .map(|span| TransformProvenanceMutationSpanV0 {
671                    source_span_start: rewrite.source_span_start + span.source_span_start,
672                    source_span_end: rewrite.source_span_start + span.source_span_end,
673                    generated_span_start: generated_window_start + span.generated_span_start,
674                    generated_span_end: generated_window_start + span.generated_span_end,
675                    node_key: None,
676                }),
677        );
678        generated_delta += rewrite.rewritten_css.len() as isize
679            - rewrite
680                .source_span_end
681                .saturating_sub(rewrite.source_span_start) as isize;
682    }
683    spans
684}
685
686fn add_signed_offset(value: usize, offset: isize) -> Option<usize> {
687    if offset >= 0 {
688        value.checked_add(offset as usize)
689    } else {
690        value.checked_sub((-offset) as usize)
691    }
692}
693
694type TransformStructuralRunnerV0 =
695    for<'a> fn(TransformStructuralPassInputV0<'a>) -> TransformPassDispatchResultV0;
696
697#[derive(Clone, Copy)]
698struct TransformStructuralPassHandlerV0 {
699    kind: TransformPassKind,
700    run: TransformStructuralRunnerV0,
701}
702
703struct TransformStructuralPassInputV0<'a> {
704    pass_id: &'static str,
705    kind: TransformPassKind,
706    decision_policy: &'static TransformStructuralDecisionPolicyV0,
707    reachability_precision: FactPrecision,
708    current_ir: &'a mut TransformIrV0,
709    input_byte_len: usize,
710    dialect: StyleDialect,
711    context: &'a TransformExecutionContextV0,
712    closed_world_bundle: Option<&'a ClosedWorldBundleV0>,
713    module_qualified_symbols: Option<&'a ModuleQualifiedSymbolSetV0>,
714    module_reachable_class_names: Option<&'a [String]>,
715}
716
717impl TransformStructuralPassInputV0<'_> {
718    fn current_ir_mut(&mut self) -> &mut TransformIrV0 {
719        self.current_ir
720    }
721
722    fn closed_world_bundle(&self) -> Option<&ClosedWorldBundleV0> {
723        self.closed_world_bundle
724    }
725
726    fn precision_blocker(&self) -> Option<TransformBlockedReasonV0> {
727        let required = self.decision_policy.required_precision()?;
728        (!self.reachability_precision.satisfies(required)).then_some(
729            TransformBlockedReasonV0::PrecisionBelowFloor {
730                required,
731                observed: self.reachability_precision,
732            },
733        )
734    }
735
736    fn ir_mutation_result(
737        &self,
738        mutation_count: usize,
739        detail: &'static str,
740    ) -> TransformPassDispatchResultV0 {
741        TransformPassDispatchResultV0::ir_mutation(
742            self.pass_id,
743            self.input_byte_len,
744            self.current_ir.source_text().len(),
745            mutation_count,
746            detail,
747        )
748    }
749
750    fn ir_rejected(&self, detail: &'static str) -> TransformPassDispatchResultV0 {
751        TransformPassDispatchResultV0::rejected(
752            self.pass_id,
753            self.input_byte_len,
754            TransformRejectionReasonV0::IrTransaction { pass: self.kind },
755            detail,
756        )
757    }
758}
759
760fn structural_pass_handlers() -> &'static [TransformStructuralPassHandlerV0] {
761    &STRUCTURAL_PASS_HANDLERS
762}
763
764struct TransformExecutionDocumentV0 {
765    current_ir: TransformIrV0,
766    dialect: StyleDialect,
767}
768
769impl TransformExecutionDocumentV0 {
770    fn new(source: &str, dialect: StyleDialect) -> Self {
771        Self {
772            current_ir: lower_transform_ir_from_source(
773                source,
774                dialect,
775                "omena-transform-passes.execution.current",
776            ),
777            dialect,
778        }
779    }
780
781    fn current_ir_mut(&mut self) -> &mut TransformIrV0 {
782        &mut self.current_ir
783    }
784
785    fn current_css(&self) -> &str {
786        self.current_ir.source_text()
787    }
788
789    fn current_byte_len(&self) -> usize {
790        self.current_css().len()
791    }
792
793    fn replace_with_css(&mut self, css: String) {
794        self.current_ir = lower_transform_ir_from_source(
795            css.as_str(),
796            self.dialect,
797            "omena-transform-passes.execution.current",
798        );
799    }
800
801    fn output_css(&self) -> String {
802        self.current_css().to_string()
803    }
804}
805
806fn transform_content_signature(source: &str) -> String {
807    blake3::hash(source.as_bytes()).to_hex().to_string()
808}
809
810static TEXT_LOCAL_PASS_HANDLERS: [TransformTextLocalPassHandlerV0; 20] = [
811    TransformTextLocalPassHandlerV0 {
812        kind: TransformPassKind::WhitespaceStrip,
813        window_scope: TransformTextLocalWindowScopeV0::DocumentTokenStream,
814        execution_mode: TransformTextLocalExecutionModeV0::FullDocument,
815        detail: "normalized lexer trivia where adjacent token boundaries remain unambiguous",
816        run: run_whitespace_strip_text_local,
817    },
818    TransformTextLocalPassHandlerV0 {
819        kind: TransformPassKind::CommentStrip,
820        window_scope: TransformTextLocalWindowScopeV0::DocumentTokenStream,
821        execution_mode: TransformTextLocalExecutionModeV0::FullDocument,
822        detail: "removed CSS block comments outside string literals",
823        run: run_comment_strip_text_local,
824    },
825    TransformTextLocalPassHandlerV0 {
826        kind: TransformPassKind::NumberCompression,
827        window_scope: TransformTextLocalWindowScopeV0::DeclarationValue,
828        execution_mode: TransformTextLocalExecutionModeV0::WindowBatch,
829        detail: "compressed lexer numeric tokens without touching identifiers or strings",
830        run: run_number_compression_text_local,
831    },
832    TransformTextLocalPassHandlerV0 {
833        kind: TransformPassKind::UnitNormalization,
834        window_scope: TransformTextLocalWindowScopeV0::DeclarationValue,
835        execution_mode: TransformTextLocalExecutionModeV0::WindowBatch,
836        detail: "normalized zero length units and known CSS unit casing inside declaration contexts",
837        run: run_unit_normalization_text_local,
838    },
839    TransformTextLocalPassHandlerV0 {
840        kind: TransformPassKind::ColorCompression,
841        window_scope: TransformTextLocalWindowScopeV0::DeclarationValue,
842        execution_mode: TransformTextLocalExecutionModeV0::WindowBatch,
843        detail: "compressed static declaration color values and hex color tokens",
844        run: run_color_compression_text_local,
845    },
846    TransformTextLocalPassHandlerV0 {
847        kind: TransformPassKind::UrlQuoteStrip,
848        window_scope: TransformTextLocalWindowScopeV0::DeclarationValue,
849        execution_mode: TransformTextLocalExecutionModeV0::WindowBatch,
850        detail: "stripped quotes from safe url() string arguments",
851        run: run_url_quote_strip_text_local,
852    },
853    TransformTextLocalPassHandlerV0 {
854        kind: TransformPassKind::StringQuoteNormalize,
855        window_scope: TransformTextLocalWindowScopeV0::DeclarationValue,
856        execution_mode: TransformTextLocalExecutionModeV0::WindowBatch,
857        detail: "normalized safe CSS string tokens, declaration-scoped font family strings, and static font keyword aliases",
858        run: run_string_quote_normalize_text_local,
859    },
860    TransformTextLocalPassHandlerV0 {
861        kind: TransformPassKind::SelectorIsWhereCompression,
862        window_scope: TransformTextLocalWindowScopeV0::Selector,
863        execution_mode: TransformTextLocalExecutionModeV0::FullDocument,
864        detail: "compressed :is/:where selector functions and keyframe selector aliases only when matching semantics are preserved",
865        run: run_selector_is_where_compression_text_local,
866    },
867    TransformTextLocalPassHandlerV0 {
868        kind: TransformPassKind::ShorthandCombining,
869        window_scope: TransformTextLocalWindowScopeV0::DeclarationBlock,
870        execution_mode: TransformTextLocalExecutionModeV0::WindowBatch,
871        detail: "combined safe shorthand declarations and adjacent longhands only with cascade-preserving proofs",
872        run: run_shorthand_combining_text_local,
873    },
874    TransformTextLocalPassHandlerV0 {
875        kind: TransformPassKind::VendorPrefixing,
876        window_scope: TransformTextLocalWindowScopeV0::DeclarationBlock,
877        execution_mode: TransformTextLocalExecutionModeV0::FullDocument,
878        detail: "inserted target-aware vendor-prefixed declaration synonyms when absent",
879        run: run_vendor_prefixing_text_local,
880    },
881    TransformTextLocalPassHandlerV0 {
882        kind: TransformPassKind::StalePrefixRemoval,
883        window_scope: TransformTextLocalWindowScopeV0::DeclarationBlock,
884        execution_mode: TransformTextLocalExecutionModeV0::WindowBatch,
885        detail: "removed explicit stale prefixed declarations only when an exact unprefixed peer proves equivalence",
886        run: run_stale_prefix_removal_text_local,
887    },
888    TransformTextLocalPassHandlerV0 {
889        kind: TransformPassKind::LightDarkLowering,
890        window_scope: TransformTextLocalWindowScopeV0::DeclarationValue,
891        execution_mode: TransformTextLocalExecutionModeV0::WindowBatch,
892        detail: "lowered light-dark() color references into dark media branches",
893        run: run_light_dark_lowering_text_local,
894    },
895    TransformTextLocalPassHandlerV0 {
896        kind: TransformPassKind::ColorMixLowering,
897        window_scope: TransformTextLocalWindowScopeV0::DeclarationValue,
898        execution_mode: TransformTextLocalExecutionModeV0::WindowBatch,
899        detail: "lowered static srgb color-mix() references with static color operands",
900        run: run_color_mix_lowering_text_local,
901    },
902    TransformTextLocalPassHandlerV0 {
903        kind: TransformPassKind::OklchOklabLowering,
904        window_scope: TransformTextLocalWindowScopeV0::DeclarationValue,
905        execution_mode: TransformTextLocalExecutionModeV0::WindowBatch,
906        detail: "lowered in-gamut oklab()/oklch() color references to srgb",
907        run: run_oklch_oklab_lowering_text_local,
908    },
909    TransformTextLocalPassHandlerV0 {
910        kind: TransformPassKind::ColorFunctionLowering,
911        window_scope: TransformTextLocalWindowScopeV0::DeclarationValue,
912        execution_mode: TransformTextLocalExecutionModeV0::WindowBatch,
913        detail: "lowered static color(...) references with static channels",
914        run: run_color_function_lowering_text_local,
915    },
916    TransformTextLocalPassHandlerV0 {
917        kind: TransformPassKind::RelativeColorLowering,
918        window_scope: TransformTextLocalWindowScopeV0::DeclarationValue,
919        execution_mode: TransformTextLocalExecutionModeV0::WindowBatch,
920        detail: "lowered static rgb(from ...) relative-color references to absolute srgb",
921        run: run_relative_color_lowering_text_local,
922    },
923    TransformTextLocalPassHandlerV0 {
924        kind: TransformPassKind::LogicalToPhysical,
925        window_scope: TransformTextLocalWindowScopeV0::DeclarationBlock,
926        execution_mode: TransformTextLocalExecutionModeV0::WindowBatch,
927        detail: "lowered logical properties only under static horizontal writing direction",
928        run: run_logical_to_physical_text_local,
929    },
930    TransformTextLocalPassHandlerV0 {
931        kind: TransformPassKind::ValueResolution,
932        window_scope: TransformTextLocalWindowScopeV0::DeclarationValue,
933        execution_mode: TransformTextLocalExecutionModeV0::FullDocument,
934        detail: "resolved whole-value references from unique local literal CSS Modules @value declarations",
935        run: run_value_resolution_text_local,
936    },
937    TransformTextLocalPassHandlerV0 {
938        kind: TransformPassKind::StaticVarSubstitution,
939        window_scope: TransformTextLocalWindowScopeV0::DeclarationValue,
940        execution_mode: TransformTextLocalExecutionModeV0::FullDocument,
941        detail: "resolved whole-value var() references from unique static :root custom properties",
942        run: run_static_var_substitution_text_local,
943    },
944    TransformTextLocalPassHandlerV0 {
945        kind: TransformPassKind::CalcReduction,
946        window_scope: TransformTextLocalWindowScopeV0::DeclarationValue,
947        execution_mode: TransformTextLocalExecutionModeV0::WindowBatch,
948        detail: "reduced whole-value CSS math functions with static same-unit arithmetic and identity operations",
949        run: run_calc_reduction_text_local,
950    },
951];
952
953static STRUCTURAL_PASS_HANDLERS: [TransformStructuralPassHandlerV0; 21] = [
954    TransformStructuralPassHandlerV0 {
955        kind: TransformPassKind::ImportInline,
956        run: run_import_inline_structural,
957    },
958    TransformStructuralPassHandlerV0 {
959        kind: TransformPassKind::ResolveCssModulesComposes,
960        run: run_resolve_css_modules_composes_structural,
961    },
962    TransformStructuralPassHandlerV0 {
963        kind: TransformPassKind::DesignTokenRouting,
964        run: run_design_token_routing_structural,
965    },
966    TransformStructuralPassHandlerV0 {
967        kind: TransformPassKind::HashCssModuleClassNames,
968        run: run_hash_css_module_class_names_structural,
969    },
970    TransformStructuralPassHandlerV0 {
971        kind: TransformPassKind::RuleDeduplication,
972        run: run_rule_deduplication_structural,
973    },
974    TransformStructuralPassHandlerV0 {
975        kind: TransformPassKind::RuleMerging,
976        run: run_rule_merging_structural,
977    },
978    TransformStructuralPassHandlerV0 {
979        kind: TransformPassKind::SelectorMerging,
980        run: run_selector_merging_structural,
981    },
982    TransformStructuralPassHandlerV0 {
983        kind: TransformPassKind::NestingUnwrap,
984        run: run_nesting_unwrap_structural,
985    },
986    TransformStructuralPassHandlerV0 {
987        kind: TransformPassKind::ScopeFlatten,
988        run: run_scope_flatten_structural,
989    },
990    TransformStructuralPassHandlerV0 {
991        kind: TransformPassKind::LayerFlatten,
992        run: run_layer_flatten_structural,
993    },
994    TransformStructuralPassHandlerV0 {
995        kind: TransformPassKind::SupportsStaticEval,
996        run: run_supports_static_eval_structural,
997    },
998    TransformStructuralPassHandlerV0 {
999        kind: TransformPassKind::MediaStaticEval,
1000        run: run_media_static_eval_structural,
1001    },
1002    TransformStructuralPassHandlerV0 {
1003        kind: TransformPassKind::ContainerStaticEval,
1004        run: run_container_static_eval_structural,
1005    },
1006    TransformStructuralPassHandlerV0 {
1007        kind: TransformPassKind::NativeCssStaticEval,
1008        run: run_native_css_static_eval_structural,
1009    },
1010    TransformStructuralPassHandlerV0 {
1011        kind: TransformPassKind::DeadMediaBranchRemoval,
1012        run: run_dead_media_branch_removal_structural,
1013    },
1014    TransformStructuralPassHandlerV0 {
1015        kind: TransformPassKind::DeadSupportsBranchRemoval,
1016        run: run_dead_supports_branch_removal_structural,
1017    },
1018    TransformStructuralPassHandlerV0 {
1019        kind: TransformPassKind::TreeShakeClass,
1020        run: run_tree_shake_class_structural,
1021    },
1022    TransformStructuralPassHandlerV0 {
1023        kind: TransformPassKind::TreeShakeKeyframes,
1024        run: run_tree_shake_keyframes_structural,
1025    },
1026    TransformStructuralPassHandlerV0 {
1027        kind: TransformPassKind::TreeShakeValue,
1028        run: run_tree_shake_value_structural,
1029    },
1030    TransformStructuralPassHandlerV0 {
1031        kind: TransformPassKind::TreeShakeCustomProperty,
1032        run: run_tree_shake_custom_property_structural,
1033    },
1034    TransformStructuralPassHandlerV0 {
1035        kind: TransformPassKind::EmptyRuleRemoval,
1036        run: run_empty_rule_removal_structural,
1037    },
1038];
1039
1040pub fn execute_transform_passes_on_source(
1041    source: &str,
1042    requested: &[TransformPassKind],
1043) -> TransformExecutionSummaryV0 {
1044    execute_transform_passes_on_source_with_dialect(source, StyleDialect::Css, requested)
1045}
1046
1047pub fn execute_transform_passes_on_source_with_dialect(
1048    source: &str,
1049    dialect: StyleDialect,
1050    requested: &[TransformPassKind],
1051) -> TransformExecutionSummaryV0 {
1052    let context = TransformExecutionContextV0::default();
1053    execute_transform_passes_on_source_with_dialect_and_context(
1054        source, dialect, requested, &context,
1055    )
1056}
1057
1058#[cfg(feature = "transform-catalog-trace")]
1059pub fn execute_transform_passes_on_source_with_transform_catalog_trace(
1060    source: &str,
1061    requested: &[TransformPassKind],
1062) -> (
1063    TransformExecutionSummaryV0,
1064    omena_lawvere::TransformCatalogModelTraceV0,
1065) {
1066    execute_transform_passes_on_source_with_transform_catalog_trace_and_dialect(
1067        source,
1068        StyleDialect::Css,
1069        requested,
1070    )
1071}
1072
1073#[cfg(feature = "transform-catalog-trace")]
1074pub fn execute_transform_passes_on_source_with_transform_catalog_trace_and_dialect(
1075    source: &str,
1076    dialect: StyleDialect,
1077    requested: &[TransformPassKind],
1078) -> (
1079    TransformExecutionSummaryV0,
1080    omena_lawvere::TransformCatalogModelTraceV0,
1081) {
1082    let summary = execute_transform_passes_on_source_with_dialect(source, dialect, requested);
1083    let trace = omena_lawvere::trace_transform_catalog_model_v0(
1084        requested,
1085        summary.ordered_pass_ids.clone(),
1086    );
1087    (summary, trace)
1088}
1089
1090#[cfg(feature = "transform-catalog-trace")]
1091pub fn evaluate_transform_catalog_reorderability_with_differential_corpus(
1092    left: TransformPassKind,
1093    right: TransformPassKind,
1094    fixtures: &[&str],
1095) -> (
1096    omena_lawvere::ReorderabilityCertificateV0,
1097    omena_lawvere::TransformCatalogDifferentialCommutativityWitnessV0,
1098) {
1099    let cases = fixtures
1100        .iter()
1101        .enumerate()
1102        .map(|(index, source)| {
1103            let left_first = execute_transform_passes_on_source(source, &[left]);
1104            let left_then_right =
1105                execute_transform_passes_on_source(&left_first.output_css, &[right]);
1106            let right_first = execute_transform_passes_on_source(source, &[right]);
1107            let right_then_left =
1108                execute_transform_passes_on_source(&right_first.output_css, &[left]);
1109            let left_then_right_mutation_count =
1110                left_first.mutation_count + left_then_right.mutation_count;
1111            let right_then_left_mutation_count =
1112                right_first.mutation_count + right_then_left.mutation_count;
1113
1114            omena_lawvere::TransformCatalogDifferentialCommutativityCaseV0 {
1115                label: format!("fixture-{index}"),
1116                input_css: (*source).to_string(),
1117                left_then_right_css: left_then_right.output_css.clone(),
1118                right_then_left_css: right_then_left.output_css.clone(),
1119                left_then_right_mutation_count,
1120                right_then_left_mutation_count,
1121                equal_output: left_then_right.output_css == right_then_left.output_css,
1122            }
1123        })
1124        .collect::<Vec<_>>();
1125    let witness =
1126        omena_lawvere::transform_catalog_differential_commutativity_witness_v0(left, right, cases);
1127    let certificate =
1128        omena_lawvere::transform_catalog_reorderability_certificate_from_differential_v0(
1129            left, right, &witness,
1130        );
1131    (certificate, witness)
1132}
1133
1134#[cfg(feature = "transform-catalog-trace")]
1135#[allow(deprecated)]
1136#[deprecated(
1137    since = "0.4.0",
1138    note = "use execute_transform_passes_on_source_with_transform_catalog_trace; removal is not before 1.0 and requires downstream migration plus zero audited non-compatibility uses"
1139)]
1140pub fn execute_transform_passes_on_source_with_lawvere_trace(
1141    source: &str,
1142    requested: &[TransformPassKind],
1143) -> (
1144    TransformExecutionSummaryV0,
1145    omena_lawvere::LawvereModelTraceV0,
1146) {
1147    execute_transform_passes_on_source_with_lawvere_trace_and_dialect(
1148        source,
1149        StyleDialect::Css,
1150        requested,
1151    )
1152}
1153
1154#[cfg(feature = "transform-catalog-trace")]
1155#[allow(deprecated)]
1156#[deprecated(
1157    since = "0.4.0",
1158    note = "use execute_transform_passes_on_source_with_transform_catalog_trace_and_dialect; removal is not before 1.0 and requires downstream migration plus zero audited non-compatibility uses"
1159)]
1160pub fn execute_transform_passes_on_source_with_lawvere_trace_and_dialect(
1161    source: &str,
1162    dialect: StyleDialect,
1163    requested: &[TransformPassKind],
1164) -> (
1165    TransformExecutionSummaryV0,
1166    omena_lawvere::LawvereModelTraceV0,
1167) {
1168    let summary = execute_transform_passes_on_source_with_dialect(source, dialect, requested);
1169    let trace = omena_lawvere::trace_lawvere_model_v0(requested, summary.ordered_pass_ids.clone());
1170    (summary, trace)
1171}
1172
1173#[cfg(feature = "transform-catalog-trace")]
1174#[allow(deprecated)]
1175#[deprecated(
1176    since = "0.4.0",
1177    note = "use evaluate_transform_catalog_reorderability_with_differential_corpus; removal is not before 1.0 and requires downstream migration plus zero audited non-compatibility uses"
1178)]
1179pub fn evaluate_lawvere_reorderability_with_differential_corpus(
1180    left: TransformPassKind,
1181    right: TransformPassKind,
1182    fixtures: &[&str],
1183) -> (
1184    omena_lawvere::ReorderabilityCertificateV0,
1185    omena_lawvere::LawvereDifferentialCommutativityWitnessV0,
1186) {
1187    let cases = fixtures
1188        .iter()
1189        .enumerate()
1190        .map(|(index, source)| {
1191            let left_first = execute_transform_passes_on_source(source, &[left]);
1192            let left_then_right =
1193                execute_transform_passes_on_source(&left_first.output_css, &[right]);
1194            let right_first = execute_transform_passes_on_source(source, &[right]);
1195            let right_then_left =
1196                execute_transform_passes_on_source(&right_first.output_css, &[left]);
1197            let left_then_right_mutation_count =
1198                left_first.mutation_count + left_then_right.mutation_count;
1199            let right_then_left_mutation_count =
1200                right_first.mutation_count + right_then_left.mutation_count;
1201
1202            omena_lawvere::LawvereDifferentialCommutativityCaseV0 {
1203                label: format!("fixture-{index}"),
1204                input_css: (*source).to_string(),
1205                left_then_right_css: left_then_right.output_css.clone(),
1206                right_then_left_css: right_then_left.output_css.clone(),
1207                left_then_right_mutation_count,
1208                right_then_left_mutation_count,
1209                equal_output: left_then_right.output_css == right_then_left.output_css,
1210            }
1211        })
1212        .collect::<Vec<_>>();
1213    let witness = omena_lawvere::lawvere_differential_commutativity_witness_v0(left, right, cases);
1214    let certificate =
1215        omena_lawvere::reorderability_certificate_from_differential_v0(left, right, &witness);
1216    (certificate, witness)
1217}
1218
1219pub fn execute_transform_passes_on_source_with_dialect_and_context(
1220    source: &str,
1221    dialect: StyleDialect,
1222    requested: &[TransformPassKind],
1223    context: &TransformExecutionContextV0,
1224) -> TransformExecutionSummaryV0 {
1225    execute_transform_passes_on_source_with_dialect_context_and_policy(
1226        source,
1227        dialect,
1228        requested,
1229        context,
1230        &TransformExecutionPolicyV0::default(),
1231    )
1232}
1233
1234pub fn execute_transform_passes_on_source_with_dialect_context_and_policy(
1235    source: &str,
1236    dialect: StyleDialect,
1237    requested: &[TransformPassKind],
1238    context: &TransformExecutionContextV0,
1239    execution_policy: &TransformExecutionPolicyV0,
1240) -> TransformExecutionSummaryV0 {
1241    super::lex_cache::with_transform_lex_cache(|| {
1242        execute_transform_passes_on_source_with_active_lex_cache(
1243            source,
1244            dialect,
1245            requested,
1246            context,
1247            None,
1248            None,
1249            TransformExecutionRuntimePolicyV0 {
1250                verification: execution_policy,
1251                semantic_trust_recording: TransformSemanticTrustRecordingV0::Record,
1252                module_qualified_symbols: None,
1253                retained_class_names: &[],
1254                token_ownership_census: None,
1255                token_ownership_module_instance: None,
1256            },
1257        )
1258    })
1259}
1260
1261pub fn execute_transform_passes_on_source_with_dialect_context_and_closed_world_bundle(
1262    source: &str,
1263    dialect: StyleDialect,
1264    requested: &[TransformPassKind],
1265    context: &TransformExecutionContextV0,
1266    closed_world_bundle: &ClosedWorldBundleV0,
1267) -> TransformExecutionSummaryV0 {
1268    execute_transform_passes_on_source_with_dialect_context_closed_world_bundle_and_precision(
1269        source,
1270        dialect,
1271        requested,
1272        context,
1273        closed_world_bundle,
1274        classify_transform_reachability_precision(context, true, None),
1275    )
1276}
1277
1278pub fn execute_transform_passes_on_source_with_dialect_context_closed_world_bundle_and_precision(
1279    source: &str,
1280    dialect: StyleDialect,
1281    requested: &[TransformPassKind],
1282    context: &TransformExecutionContextV0,
1283    closed_world_bundle: &ClosedWorldBundleV0,
1284    reachability_precision: FactPrecision,
1285) -> TransformExecutionSummaryV0 {
1286    execute_transform_passes_on_source_with_dialect_context_closed_world_bundle_precision_and_policy(
1287        source,
1288        dialect,
1289        requested,
1290        context,
1291        closed_world_bundle,
1292        reachability_precision,
1293        &TransformExecutionPolicyV0::default(),
1294    )
1295}
1296
1297pub fn execute_transform_passes_on_source_with_dialect_context_closed_world_bundle_precision_and_policy(
1298    source: &str,
1299    dialect: StyleDialect,
1300    requested: &[TransformPassKind],
1301    context: &TransformExecutionContextV0,
1302    closed_world_bundle: &ClosedWorldBundleV0,
1303    reachability_precision: FactPrecision,
1304    execution_policy: &TransformExecutionPolicyV0,
1305) -> TransformExecutionSummaryV0 {
1306    super::lex_cache::with_transform_lex_cache(|| {
1307        execute_transform_passes_on_source_with_active_lex_cache(
1308            source,
1309            dialect,
1310            requested,
1311            context,
1312            Some(closed_world_bundle),
1313            Some(reachability_precision),
1314            TransformExecutionRuntimePolicyV0 {
1315                verification: execution_policy,
1316                semantic_trust_recording: TransformSemanticTrustRecordingV0::Record,
1317                module_qualified_symbols: None,
1318                retained_class_names: &[],
1319                token_ownership_census: None,
1320                token_ownership_module_instance: None,
1321            },
1322        )
1323    })
1324}
1325
1326pub fn execute_transform_passes_on_module_with_dialect_context_and_closed_world_bundle(
1327    source: &str,
1328    dialect: StyleDialect,
1329    requested: &[TransformPassKind],
1330    context: &TransformExecutionContextV0,
1331    closed_world_bundle: &ClosedWorldBundleV0,
1332    module_instance: &ModuleInstanceKeyV0,
1333) -> Result<TransformExecutionSummaryV0, TransformModuleQualifiedExecutionErrorV0> {
1334    execute_transform_passes_on_module_with_dialect_context_policy_and_closed_world_bundle(
1335        source,
1336        dialect,
1337        requested,
1338        context,
1339        closed_world_bundle,
1340        module_instance,
1341        classify_transform_reachability_precision(context, true, None),
1342        &TransformExecutionPolicyV0::default(),
1343    )
1344}
1345
1346// Keep each execution authority explicit at this compatibility boundary.
1347#[allow(clippy::too_many_arguments)]
1348pub fn execute_transform_passes_on_module_with_dialect_context_policy_and_closed_world_bundle(
1349    source: &str,
1350    dialect: StyleDialect,
1351    requested: &[TransformPassKind],
1352    context: &TransformExecutionContextV0,
1353    closed_world_bundle: &ClosedWorldBundleV0,
1354    module_instance: &ModuleInstanceKeyV0,
1355    reachability_precision: FactPrecision,
1356    execution_policy: &TransformExecutionPolicyV0,
1357) -> Result<TransformExecutionSummaryV0, TransformModuleQualifiedExecutionErrorV0> {
1358    execute_transform_passes_on_module_with_dialect_context_policy_and_closed_world_bundle_and_retained_class_names(
1359        source,
1360        dialect,
1361        requested,
1362        context,
1363        closed_world_bundle,
1364        module_instance,
1365        reachability_precision,
1366        execution_policy,
1367        &[],
1368    )
1369}
1370
1371// Keep each execution authority explicit at this compatibility boundary.
1372#[allow(clippy::too_many_arguments)]
1373pub fn execute_transform_passes_on_module_with_dialect_context_policy_and_closed_world_bundle_and_retained_class_names(
1374    source: &str,
1375    dialect: StyleDialect,
1376    requested: &[TransformPassKind],
1377    context: &TransformExecutionContextV0,
1378    closed_world_bundle: &ClosedWorldBundleV0,
1379    module_instance: &ModuleInstanceKeyV0,
1380    reachability_precision: FactPrecision,
1381    execution_policy: &TransformExecutionPolicyV0,
1382    retained_class_names: &[String],
1383) -> Result<TransformExecutionSummaryV0, TransformModuleQualifiedExecutionErrorV0> {
1384    execute_transform_passes_on_module_with_optional_token_ownership_census(
1385        source,
1386        dialect,
1387        requested,
1388        context,
1389        closed_world_bundle,
1390        module_instance,
1391        reachability_precision,
1392        execution_policy,
1393        retained_class_names,
1394        None,
1395        None,
1396    )
1397}
1398
1399impl CssModuleTokenOwnershipCensusV0 {
1400    /// Executes one module while using this product census as ownership admission authority.
1401    #[allow(clippy::too_many_arguments)]
1402    pub fn execute_module_transform_passes_with_ownership_admission(
1403        &self,
1404        source: &str,
1405        dialect: StyleDialect,
1406        requested: &[TransformPassKind],
1407        context: &TransformExecutionContextV0,
1408        closed_world_bundle: &ClosedWorldBundleV0,
1409        module_instance: &ModuleInstanceKeyV0,
1410        reachability_precision: FactPrecision,
1411        execution_policy: &TransformExecutionPolicyV0,
1412        retained_class_names: &[String],
1413    ) -> Result<TransformExecutionSummaryV0, TransformModuleQualifiedExecutionErrorV0> {
1414        self.execute_module_transform_passes_with_ownership_admission_for_identity(
1415            source,
1416            dialect,
1417            requested,
1418            context,
1419            closed_world_bundle,
1420            module_instance,
1421            module_instance,
1422            reachability_precision,
1423            execution_policy,
1424            retained_class_names,
1425        )
1426    }
1427
1428    /// Executes one module while comparing ownership evidence in its census identity space.
1429    ///
1430    /// `module_instance` remains the sealed-bundle lookup and diagnostic identity. The separate
1431    /// `ownership_module_instance` is the identity used by the census producer, which can be
1432    /// workspace-root relative even when bundle keys are absolute.
1433    #[allow(clippy::too_many_arguments)]
1434    pub fn execute_module_transform_passes_with_ownership_admission_for_identity(
1435        &self,
1436        source: &str,
1437        dialect: StyleDialect,
1438        requested: &[TransformPassKind],
1439        context: &TransformExecutionContextV0,
1440        closed_world_bundle: &ClosedWorldBundleV0,
1441        module_instance: &ModuleInstanceKeyV0,
1442        ownership_module_instance: &ModuleInstanceKeyV0,
1443        reachability_precision: FactPrecision,
1444        execution_policy: &TransformExecutionPolicyV0,
1445        retained_class_names: &[String],
1446    ) -> Result<TransformExecutionSummaryV0, TransformModuleQualifiedExecutionErrorV0> {
1447        execute_transform_passes_on_module_with_optional_token_ownership_census(
1448            source,
1449            dialect,
1450            requested,
1451            context,
1452            closed_world_bundle,
1453            module_instance,
1454            reachability_precision,
1455            execution_policy,
1456            retained_class_names,
1457            Some(self),
1458            Some(ownership_module_instance),
1459        )
1460    }
1461}
1462
1463#[allow(clippy::too_many_arguments)]
1464fn execute_transform_passes_on_module_with_optional_token_ownership_census(
1465    source: &str,
1466    dialect: StyleDialect,
1467    requested: &[TransformPassKind],
1468    context: &TransformExecutionContextV0,
1469    closed_world_bundle: &ClosedWorldBundleV0,
1470    module_instance: &ModuleInstanceKeyV0,
1471    reachability_precision: FactPrecision,
1472    execution_policy: &TransformExecutionPolicyV0,
1473    retained_class_names: &[String],
1474    token_ownership_census: Option<&CssModuleTokenOwnershipCensusV0>,
1475    token_ownership_module_instance: Option<&ModuleInstanceKeyV0>,
1476) -> Result<TransformExecutionSummaryV0, TransformModuleQualifiedExecutionErrorV0> {
1477    let Some(module_qualified_symbols) = closed_world_bundle
1478        .reachability()
1479        .symbols_for_module(module_instance)
1480    else {
1481        return Err(
1482            TransformModuleQualifiedExecutionErrorV0::UnknownModuleInstance {
1483                module_instance: module_instance.clone(),
1484            },
1485        );
1486    };
1487
1488    Ok(super::lex_cache::with_transform_lex_cache(|| {
1489        execute_transform_passes_on_source_with_active_lex_cache(
1490            source,
1491            dialect,
1492            requested,
1493            context,
1494            Some(closed_world_bundle),
1495            Some(reachability_precision),
1496            TransformExecutionRuntimePolicyV0 {
1497                verification: execution_policy,
1498                semantic_trust_recording: TransformSemanticTrustRecordingV0::Record,
1499                module_qualified_symbols: Some(module_qualified_symbols),
1500                retained_class_names,
1501                token_ownership_census,
1502                token_ownership_module_instance,
1503            },
1504        )
1505    }))
1506}
1507
1508#[doc(hidden)]
1509pub fn execute_transform_passes_on_source_with_dialect_and_context_without_lex_cache_for_measurement(
1510    source: &str,
1511    dialect: StyleDialect,
1512    requested: &[TransformPassKind],
1513    context: &TransformExecutionContextV0,
1514) -> TransformExecutionSummaryV0 {
1515    execute_transform_passes_on_source_with_active_lex_cache(
1516        source,
1517        dialect,
1518        requested,
1519        context,
1520        None,
1521        None,
1522        TransformExecutionRuntimePolicyV0 {
1523            verification: &TransformExecutionPolicyV0::default(),
1524            semantic_trust_recording: TransformSemanticTrustRecordingV0::Record,
1525            module_qualified_symbols: None,
1526            retained_class_names: &[],
1527            token_ownership_census: None,
1528            token_ownership_module_instance: None,
1529        },
1530    )
1531}
1532
1533#[doc(hidden)]
1534pub fn execute_transform_passes_on_source_with_dialect_and_context_without_semantic_trust_for_measurement(
1535    source: &str,
1536    dialect: StyleDialect,
1537    requested: &[TransformPassKind],
1538    context: &TransformExecutionContextV0,
1539) -> TransformExecutionSummaryV0 {
1540    super::lex_cache::with_transform_lex_cache(|| {
1541        execute_transform_passes_on_source_with_active_lex_cache(
1542            source,
1543            dialect,
1544            requested,
1545            context,
1546            None,
1547            None,
1548            TransformExecutionRuntimePolicyV0 {
1549                verification: &TransformExecutionPolicyV0::default(),
1550                semantic_trust_recording: TransformSemanticTrustRecordingV0::OmitForMeasurement,
1551                module_qualified_symbols: None,
1552                retained_class_names: &[],
1553                token_ownership_census: None,
1554                token_ownership_module_instance: None,
1555            },
1556        )
1557    })
1558}
1559
1560fn dispatch_text_local_pass(
1561    pass_id: &'static str,
1562    handler: TransformTextLocalPassHandlerV0,
1563    current_ir: &TransformIrV0,
1564    dialect: StyleDialect,
1565    context: &TransformExecutionContextV0,
1566) -> Option<TransformPassDispatchResultV0> {
1567    debug_assert_eq!(
1568        omena_transform_cst::transform_pass_class(handler.kind),
1569        TransformPassClassV0::TextLocal
1570    );
1571    let input = TransformTextLocalPassInputV0::from_ir(
1572        current_ir,
1573        handler.window_scope,
1574        handler.execution_mode,
1575        dialect,
1576        context,
1577    );
1578    let output = (handler.run)(input);
1579    let input_byte_len = output.input_byte_len();
1580    let mutation_count = output.mutation_count;
1581    let provenance_mutation_spans = output.provenance_mutation_spans().to_vec();
1582    let next_css = output.into_document_css();
1583    let mut result = TransformPassDispatchResultV0::textual_mutation(
1584        pass_id,
1585        input_byte_len,
1586        next_css,
1587        mutation_count,
1588        handler.detail,
1589    );
1590    if !provenance_mutation_spans.is_empty() {
1591        result.provenance_mutation_spans = Some(provenance_mutation_spans);
1592    }
1593    Some(result)
1594}
1595
1596fn run_whitespace_strip_text_local(
1597    input: TransformTextLocalPassInputV0<'_>,
1598) -> TransformTextLocalPassOutputV0<'_> {
1599    input.rewrite_text_local(|source, dialect, _context| normalize_css_whitespace(source, dialect))
1600}
1601
1602fn run_comment_strip_text_local(
1603    input: TransformTextLocalPassInputV0<'_>,
1604) -> TransformTextLocalPassOutputV0<'_> {
1605    input.rewrite_text_local(|source, dialect, _context| strip_css_comments(source, dialect))
1606}
1607
1608fn run_number_compression_text_local(
1609    input: TransformTextLocalPassInputV0<'_>,
1610) -> TransformTextLocalPassOutputV0<'_> {
1611    input.rewrite_text_local(|source, dialect, _context| compress_css_numbers(source, dialect))
1612}
1613
1614fn run_unit_normalization_text_local(
1615    input: TransformTextLocalPassInputV0<'_>,
1616) -> TransformTextLocalPassOutputV0<'_> {
1617    input.rewrite_text_local(|source, dialect, _context| normalize_css_units(source, dialect))
1618}
1619
1620fn run_color_compression_text_local(
1621    input: TransformTextLocalPassInputV0<'_>,
1622) -> TransformTextLocalPassOutputV0<'_> {
1623    input.rewrite_text_local(|source, dialect, _context| compress_css_colors(source, dialect))
1624}
1625
1626fn run_url_quote_strip_text_local(
1627    input: TransformTextLocalPassInputV0<'_>,
1628) -> TransformTextLocalPassOutputV0<'_> {
1629    input.rewrite_text_local(|source, dialect, _context| strip_css_url_quotes(source, dialect))
1630}
1631
1632fn run_string_quote_normalize_text_local(
1633    input: TransformTextLocalPassInputV0<'_>,
1634) -> TransformTextLocalPassOutputV0<'_> {
1635    input.rewrite_text_local(|source, dialect, _context| {
1636        normalize_css_string_quotes(source, dialect)
1637    })
1638}
1639
1640fn run_selector_is_where_compression_text_local(
1641    input: TransformTextLocalPassInputV0<'_>,
1642) -> TransformTextLocalPassOutputV0<'_> {
1643    input.rewrite_text_local(|source, dialect, _context| {
1644        compress_css_is_where_selectors(source, dialect)
1645    })
1646}
1647
1648fn run_shorthand_combining_text_local(
1649    input: TransformTextLocalPassInputV0<'_>,
1650) -> TransformTextLocalPassOutputV0<'_> {
1651    input.rewrite_text_local(|source, dialect, _context| combine_css_shorthands(source, dialect))
1652}
1653
1654fn run_vendor_prefixing_text_local(
1655    input: TransformTextLocalPassInputV0<'_>,
1656) -> TransformTextLocalPassOutputV0<'_> {
1657    input.rewrite_text_local(|source, dialect, context| {
1658        let vendor_prefix_policy = context
1659            .vendor_prefix_policy
1660            .unwrap_or_else(TransformVendorPrefixPolicyV0::conservative);
1661        add_css_vendor_prefixes(source, dialect, vendor_prefix_policy)
1662    })
1663}
1664
1665fn run_stale_prefix_removal_text_local(
1666    input: TransformTextLocalPassInputV0<'_>,
1667) -> TransformTextLocalPassOutputV0<'_> {
1668    input.rewrite_text_local(|source, dialect, _context| {
1669        remove_stale_css_vendor_prefixes(source, dialect)
1670    })
1671}
1672
1673fn run_light_dark_lowering_text_local(
1674    input: TransformTextLocalPassInputV0<'_>,
1675) -> TransformTextLocalPassOutputV0<'_> {
1676    input.rewrite_text_local(|source, dialect, _context| lower_css_light_dark(source, dialect))
1677}
1678
1679fn run_color_mix_lowering_text_local(
1680    input: TransformTextLocalPassInputV0<'_>,
1681) -> TransformTextLocalPassOutputV0<'_> {
1682    input.rewrite_text_local(|source, dialect, _context| lower_css_color_mix(source, dialect))
1683}
1684
1685fn run_oklch_oklab_lowering_text_local(
1686    input: TransformTextLocalPassInputV0<'_>,
1687) -> TransformTextLocalPassOutputV0<'_> {
1688    input.rewrite_text_local(|source, dialect, _context| lower_css_oklab_oklch(source, dialect))
1689}
1690
1691fn run_color_function_lowering_text_local(
1692    input: TransformTextLocalPassInputV0<'_>,
1693) -> TransformTextLocalPassOutputV0<'_> {
1694    input.rewrite_text_local(|source, dialect, _context| lower_css_color_function(source, dialect))
1695}
1696
1697fn run_relative_color_lowering_text_local(
1698    input: TransformTextLocalPassInputV0<'_>,
1699) -> TransformTextLocalPassOutputV0<'_> {
1700    input.rewrite_text_local(|source, dialect, _context| lower_relative_color(source, dialect))
1701}
1702
1703fn run_logical_to_physical_text_local(
1704    input: TransformTextLocalPassInputV0<'_>,
1705) -> TransformTextLocalPassOutputV0<'_> {
1706    input.rewrite_text_local(|source, dialect, _context| {
1707        lower_css_logical_to_physical(source, dialect)
1708    })
1709}
1710
1711fn run_value_resolution_text_local(
1712    input: TransformTextLocalPassInputV0<'_>,
1713) -> TransformTextLocalPassOutputV0<'_> {
1714    input.rewrite_text_local(|source, dialect, context| {
1715        resolve_static_css_modules_values(source, dialect, &context.css_module_value_resolutions)
1716    })
1717}
1718
1719fn run_static_var_substitution_text_local(
1720    input: TransformTextLocalPassInputV0<'_>,
1721) -> TransformTextLocalPassOutputV0<'_> {
1722    input.rewrite_text_local(|source, dialect, _context| {
1723        substitute_static_css_custom_properties(source, dialect)
1724    })
1725}
1726
1727fn run_calc_reduction_text_local(
1728    input: TransformTextLocalPassInputV0<'_>,
1729) -> TransformTextLocalPassOutputV0<'_> {
1730    input.rewrite_text_local(|source, dialect, _context| reduce_css_calc(source, dialect))
1731}
1732
1733fn dispatch_module_evaluation_pass(
1734    pass_id: &'static str,
1735    pass: TransformPassKind,
1736    input_css: &str,
1737    dialect: StyleDialect,
1738    context: &TransformExecutionContextV0,
1739) -> Option<TransformPassDispatchResultV0> {
1740    let input_byte_len = input_css.len();
1741    match pass {
1742        TransformPassKind::ScssModuleEvaluate
1743            if matches!(dialect, StyleDialect::Scss | StyleDialect::Sass) =>
1744        {
1745            if let Some(evaluation) = context.scss_module_evaluation.as_ref() {
1746                let materialized = materialize_transform_module_evaluation_output(
1747                    input_css,
1748                    evaluation,
1749                    "applied explicit SCSS module evaluation native edit output from the evaluator boundary",
1750                    "preserved SCSS source because native evaluator edits did not match the oracle boundary",
1751                );
1752                let mutation_count = usize::from(input_css != materialized.css);
1753                let mut result = TransformPassDispatchResultV0::textual_mutation(
1754                    pass_id,
1755                    input_byte_len,
1756                    materialized.css,
1757                    mutation_count,
1758                    materialized.detail,
1759                );
1760                result.css_module_evaluation = Some(evaluation.clone());
1761                Some(result)
1762            } else {
1763                Some(TransformPassDispatchResultV0::blocked(
1764                    pass_id,
1765                    input_byte_len,
1766                    TransformBlockedReasonV0::MissingPrecondition {
1767                        precondition: TransformPreconditionV0::EvaluatorOutput {
1768                            profile: TransformEvaluationProfileV0::Scss,
1769                        },
1770                    },
1771                    "requires explicit SCSS evaluator output before mutation",
1772                ))
1773            }
1774        }
1775        TransformPassKind::ScssModuleEvaluate => Some(TransformPassDispatchResultV0::profile_only(
1776            pass_id,
1777            input_byte_len,
1778            TransformEvaluationProfileV0::Scss,
1779            "requires explicit SCSS evaluator output before mutation",
1780        )),
1781        TransformPassKind::LessModuleEvaluate if dialect == StyleDialect::Less => {
1782            if let Some(evaluation) = context.less_module_evaluation.as_ref() {
1783                let materialized = materialize_transform_module_evaluation_output(
1784                    input_css,
1785                    evaluation,
1786                    "applied explicit Less module evaluation native edit output from the evaluator boundary",
1787                    "preserved Less source because native evaluator edits did not match the oracle boundary",
1788                );
1789                let mutation_count = usize::from(input_css != materialized.css);
1790                let mut result = TransformPassDispatchResultV0::textual_mutation(
1791                    pass_id,
1792                    input_byte_len,
1793                    materialized.css,
1794                    mutation_count,
1795                    materialized.detail,
1796                );
1797                result.css_module_evaluation = Some(evaluation.clone());
1798                Some(result)
1799            } else {
1800                Some(TransformPassDispatchResultV0::blocked(
1801                    pass_id,
1802                    input_byte_len,
1803                    TransformBlockedReasonV0::MissingPrecondition {
1804                        precondition: TransformPreconditionV0::EvaluatorOutput {
1805                            profile: TransformEvaluationProfileV0::Less,
1806                        },
1807                    },
1808                    "requires explicit Less evaluator output before mutation",
1809                ))
1810            }
1811        }
1812        TransformPassKind::LessModuleEvaluate => Some(TransformPassDispatchResultV0::profile_only(
1813            pass_id,
1814            input_byte_len,
1815            TransformEvaluationProfileV0::Less,
1816            "requires explicit Less evaluator output before mutation",
1817        )),
1818        _ => None,
1819    }
1820}
1821
1822fn dispatch_structural_pass(
1823    pass_id: &'static str,
1824    handler: TransformStructuralPassHandlerV0,
1825    current_ir: &mut TransformIrV0,
1826    context: TransformStructuralDispatchContextV0<'_>,
1827) -> Option<TransformPassDispatchResultV0> {
1828    debug_assert_eq!(
1829        omena_transform_cst::transform_pass_class(handler.kind),
1830        TransformPassClassV0::Structural
1831    );
1832    let decision_policy = transform_structural_decision_policy(handler.kind)?;
1833    let input_byte_len = current_ir.source_text().len();
1834    reset_structural_ir_transaction_mutation_span_batches();
1835    let mut result = (handler.run)(TransformStructuralPassInputV0 {
1836        pass_id,
1837        kind: handler.kind,
1838        decision_policy,
1839        reachability_precision: classify_transform_reachability_precision(
1840            context.execution,
1841            context.closed_world_bundle.is_some(),
1842            context.reachability_precision_ceiling,
1843        ),
1844        current_ir,
1845        input_byte_len,
1846        dialect: context.dialect,
1847        context: context.execution,
1848        closed_world_bundle: context.closed_world_bundle,
1849        module_qualified_symbols: context.module_qualified_symbols,
1850        module_reachable_class_names: context.module_reachable_class_names,
1851    });
1852    let span_batches = take_structural_ir_transaction_mutation_span_batches();
1853    if let Some(mutation_spans) = compose_ir_transaction_mutation_span_batches(
1854        input_byte_len,
1855        current_ir.source_text().len(),
1856        span_batches.as_slice(),
1857    ) {
1858        result.provenance_mutation_spans = Some(mutation_spans);
1859    }
1860    Some(result)
1861}
1862
1863#[derive(Clone, Copy)]
1864struct TransformStructuralDispatchContextV0<'a> {
1865    dialect: StyleDialect,
1866    execution: &'a TransformExecutionContextV0,
1867    closed_world_bundle: Option<&'a ClosedWorldBundleV0>,
1868    module_qualified_symbols: Option<&'a ModuleQualifiedSymbolSetV0>,
1869    module_reachable_class_names: Option<&'a [String]>,
1870    reachability_precision_ceiling: Option<FactPrecision>,
1871}
1872
1873pub fn classify_transform_reachability_precision(
1874    context: &TransformExecutionContextV0,
1875    closed_world_bundle_available: bool,
1876    precision_ceiling: Option<FactPrecision>,
1877) -> FactPrecision {
1878    let observed = if closed_world_bundle_available {
1879        // A closed bundle is conservative by default; only a witness-derived ceiling preserves Exact.
1880        if precision_ceiling == Some(FactPrecision::Exact) {
1881            FactPrecision::Exact
1882        } else {
1883            FactPrecision::Conservative
1884        }
1885    } else if !context.reachable_class_names.is_empty()
1886        || !context.reachable_keyframe_names.is_empty()
1887        || !context.reachable_value_names.is_empty()
1888        || !context.reachable_custom_property_names.is_empty()
1889    {
1890        FactPrecision::Heuristic
1891    } else {
1892        FactPrecision::Unknown
1893    };
1894    precision_ceiling.map_or(observed, |ceiling| observed.bounded_by(ceiling))
1895}
1896
1897fn dispatch_emission_pass(
1898    pass_id: &'static str,
1899    pass: TransformPassKind,
1900    input_byte_len: usize,
1901) -> Option<TransformPassDispatchResultV0> {
1902    match pass {
1903        TransformPassKind::PrintCss => Some(TransformPassDispatchResultV0::no_change(
1904            pass_id,
1905            input_byte_len,
1906            TransformNoChangeReasonV0::EmissionBoundary,
1907            "observed final emission boundary",
1908        )),
1909        _ => None,
1910    }
1911}
1912
1913fn run_import_inline_structural(
1914    mut input: TransformStructuralPassInputV0<'_>,
1915) -> TransformPassDispatchResultV0 {
1916    if input.dialect != StyleDialect::Less && input.context.import_inlines.is_empty() {
1917        return TransformPassDispatchResultV0::blocked(
1918            input.pass_id,
1919            input.input_byte_len,
1920            TransformBlockedReasonV0::MissingPrecondition {
1921                precondition: TransformPreconditionV0::ResolvedImportReplacements,
1922            },
1923            "requires explicit resolved import replacements before mutation",
1924        );
1925    }
1926    let dialect = input.dialect;
1927    let import_inlines = input.context.import_inlines.clone();
1928    let Ok(mutation_count) =
1929        inline_css_imports_in_ir(input.current_ir_mut(), dialect, &import_inlines)
1930    else {
1931        return input
1932            .ir_rejected("typed IR transaction rejected the import-inline structural rewrite");
1933    };
1934    let mut result = input.ir_mutation_result(
1935        mutation_count,
1936        "replaced resolved @import directives and optional Less imports",
1937    );
1938    result.css_import_inlines = import_inlines;
1939    result
1940}
1941
1942fn run_resolve_css_modules_composes_structural(
1943    mut input: TransformStructuralPassInputV0<'_>,
1944) -> TransformPassDispatchResultV0 {
1945    let dialect = input.dialect;
1946    let explicit_resolutions = input.context.css_module_composes_resolutions.clone();
1947    let resolutions =
1948        css_module_composes_resolutions_for_ir(input.current_ir, dialect, &explicit_resolutions);
1949    if resolutions.is_empty() {
1950        return TransformPassDispatchResultV0::blocked(
1951            input.pass_id,
1952            input.input_byte_len,
1953            TransformBlockedReasonV0::MissingPrecondition {
1954                precondition: TransformPreconditionV0::CssModulesComposesResolution,
1955            },
1956            "requires CSS Modules composes declarations or an explicit export set before mutation",
1957        );
1958    }
1959    let Ok(mutation_count) =
1960        resolve_css_module_composes_in_ir(input.current_ir_mut(), dialect, &resolutions)
1961    else {
1962        return input.ir_rejected(
1963            "typed IR transaction rejected the CSS Modules composes structural rewrite",
1964        );
1965    };
1966    let mut result = input.ir_mutation_result(
1967        mutation_count,
1968        "removed resolved CSS Modules composes declarations using an explicit export set",
1969    );
1970    result.css_module_composes_exports = resolutions;
1971    result
1972}
1973
1974fn run_design_token_routing_structural(
1975    mut input: TransformStructuralPassInputV0<'_>,
1976) -> TransformPassDispatchResultV0 {
1977    if input.context.design_token_routes.is_empty() {
1978        return TransformPassDispatchResultV0::blocked(
1979            input.pass_id,
1980            input.input_byte_len,
1981            TransformBlockedReasonV0::MissingPrecondition {
1982                precondition: TransformPreconditionV0::DesignTokenRoutes,
1983            },
1984            "requires explicit bridge design-token routes before mutation",
1985        );
1986    }
1987    let dialect = input.dialect;
1988    let design_token_routes = input.context.design_token_routes.clone();
1989    let Ok(mutation_count) =
1990        route_design_token_values_in_ir(input.current_ir_mut(), dialect, &design_token_routes)
1991    else {
1992        return input
1993            .ir_rejected("typed IR transaction rejected the design-token structural rewrite");
1994    };
1995    let mut result = input.ir_mutation_result(
1996        mutation_count,
1997        "routed whole-value design-token references through explicit bridge token routes",
1998    );
1999    result.design_token_routes = design_token_routes;
2000    result
2001}
2002
2003fn run_hash_css_module_class_names_structural(
2004    mut input: TransformStructuralPassInputV0<'_>,
2005) -> TransformPassDispatchResultV0 {
2006    let dialect = input.dialect;
2007    let class_name_rewrites = input.context.class_name_rewrites.clone();
2008    let Ok(mutation_count) =
2009        rewrite_css_module_class_names_in_ir(input.current_ir_mut(), dialect, &class_name_rewrites)
2010    else {
2011        return input.ir_rejected(
2012            "typed IR transaction rejected the CSS Modules class hashing structural rewrite",
2013        );
2014    };
2015    if mutation_count == 0 && class_name_rewrites.is_empty() {
2016        return TransformPassDispatchResultV0::blocked(
2017            input.pass_id,
2018            input.input_byte_len,
2019            TransformBlockedReasonV0::MissingPrecondition {
2020                precondition: TransformPreconditionV0::SelectorIdentity,
2021            },
2022            "requires an explicit selector identity map or CSS Modules scope markers before mutation",
2023        );
2024    }
2025    if mutation_count == 0 {
2026        return TransformPassDispatchResultV0::no_change(
2027            input.pass_id,
2028            input.input_byte_len,
2029            TransformNoChangeReasonV0::NoMatchingSelectorRewrite,
2030            "observed CSS Modules class hashing boundary without matching selector rewrites",
2031        );
2032    }
2033    input.ir_mutation_result(
2034        mutation_count,
2035        "rewrote CSS Modules class selectors and scope markers through the structural IR boundary",
2036    )
2037}
2038
2039fn run_rule_deduplication_structural(
2040    mut input: TransformStructuralPassInputV0<'_>,
2041) -> TransformPassDispatchResultV0 {
2042    let dialect = input.dialect;
2043    let mutation_count = match dedupe_exact_css_rules_in_ir(input.current_ir_mut(), dialect) {
2044        Ok(result) => result,
2045        Err(_) => {
2046            return input
2047                .ir_rejected("typed IR transaction rejected the rule deduplication rewrite");
2048        }
2049    };
2050    input.ir_mutation_result(
2051        mutation_count,
2052        "removed cascade-safe duplicate ordinary rules while preserving the final occurrence",
2053    )
2054}
2055
2056fn run_rule_merging_structural(
2057    mut input: TransformStructuralPassInputV0<'_>,
2058) -> TransformPassDispatchResultV0 {
2059    let dialect = input.dialect;
2060    let Ok(mutation_count) =
2061        merge_adjacent_same_selector_css_rules_in_ir(input.current_ir_mut(), dialect)
2062    else {
2063        return input.ir_rejected("typed IR transaction rejected the rule merging rewrite");
2064    };
2065    input.ir_mutation_result(
2066        mutation_count,
2067        "merged adjacent same-selector ordinary rule runs without reordering declarations",
2068    )
2069}
2070
2071fn run_selector_merging_structural(
2072    mut input: TransformStructuralPassInputV0<'_>,
2073) -> TransformPassDispatchResultV0 {
2074    let dialect = input.dialect;
2075    let Ok(mutation_count) =
2076        merge_adjacent_same_block_css_selectors_in_ir(input.current_ir_mut(), dialect)
2077    else {
2078        return input.ir_rejected("typed IR transaction rejected the selector merging rewrite");
2079    };
2080    input.ir_mutation_result(
2081        mutation_count,
2082        "merged adjacent ordinary rule runs with identical declaration blocks",
2083    )
2084}
2085
2086fn run_nesting_unwrap_structural(
2087    mut input: TransformStructuralPassInputV0<'_>,
2088) -> TransformPassDispatchResultV0 {
2089    let dialect = input.dialect;
2090    let Ok(mutation_count) = unwrap_css_nesting_in_ir(input.current_ir_mut(), dialect) else {
2091        return input.ir_rejected("typed IR transaction rejected the nesting structural rewrite");
2092    };
2093    input.ir_mutation_result(
2094        mutation_count,
2095        "unwrapped nested ordinary rules and conditional group rules",
2096    )
2097}
2098
2099fn run_scope_flatten_structural(
2100    mut input: TransformStructuralPassInputV0<'_>,
2101) -> TransformPassDispatchResultV0 {
2102    let dialect = input.dialect;
2103    let Ok(mutation_count) = flatten_css_scopes_in_ir(input.current_ir_mut(), dialect) else {
2104        return input.ir_rejected("typed IR transaction rejected the scope structural rewrite");
2105    };
2106    input.ir_mutation_result(
2107        mutation_count,
2108        "flattened only @scope candidates accepted by the cascade scope-flatten proof",
2109    )
2110}
2111
2112fn run_layer_flatten_structural(
2113    mut input: TransformStructuralPassInputV0<'_>,
2114) -> TransformPassDispatchResultV0 {
2115    if input.closed_world_bundle().is_some() {
2116        let dialect = input.dialect;
2117        let Ok(mutation_count) = flatten_css_layers_in_ir(input.current_ir_mut(), dialect, true)
2118        else {
2119            return input.ir_rejected("typed IR transaction rejected the layer structural rewrite");
2120        };
2121        input.ir_mutation_result(
2122            mutation_count,
2123            "flattened only @layer candidates accepted by the closed-bundle cascade proof",
2124        )
2125    } else {
2126        TransformPassDispatchResultV0::blocked(
2127            input.pass_id,
2128            input.input_byte_len,
2129            TransformBlockedReasonV0::MissingPrecondition {
2130                precondition: TransformPreconditionV0::ClosedStyleWorldBundle,
2131            },
2132            "requires an explicit closed-style-world bundle witness before mutation",
2133        )
2134    }
2135}
2136
2137fn run_supports_static_eval_structural(
2138    mut input: TransformStructuralPassInputV0<'_>,
2139) -> TransformPassDispatchResultV0 {
2140    let dialect = input.dialect;
2141    let assumption = static_supports_assumption_from_context(input.context);
2142    let Ok(mutation_count) =
2143        evaluate_static_supports_rules_in_ir(input.current_ir_mut(), dialect, assumption)
2144    else {
2145        return input
2146            .ir_rejected("typed IR transaction rejected the supports static structural rewrite");
2147    };
2148    input.ir_mutation_result(
2149        mutation_count,
2150        "evaluated simple @supports branches with cascade supports-static witness",
2151    )
2152}
2153
2154fn static_supports_assumption_from_context(
2155    context: &TransformExecutionContextV0,
2156) -> StaticSupportsAssumptionV0 {
2157    context
2158        .supports_target_capability
2159        .map(StaticSupportsAssumptionV0::TargetCapability)
2160        .unwrap_or(StaticSupportsAssumptionV0::ModernBrowser)
2161}
2162
2163fn run_media_static_eval_structural(
2164    mut input: TransformStructuralPassInputV0<'_>,
2165) -> TransformPassDispatchResultV0 {
2166    let dialect = input.dialect;
2167    let Ok(mutation_count) = evaluate_static_media_rules_in_ir(input.current_ir_mut(), dialect)
2168    else {
2169        return input
2170            .ir_rejected("typed IR transaction rejected the media static structural rewrite");
2171    };
2172    input.ir_mutation_result(
2173        mutation_count,
2174        "evaluated literal @media all/not all branches and normalized simple min/max media ranges",
2175    )
2176}
2177
2178fn run_container_static_eval_structural(
2179    mut input: TransformStructuralPassInputV0<'_>,
2180) -> TransformPassDispatchResultV0 {
2181    let dialect = input.dialect;
2182    let Ok(mutation_count) = evaluate_static_container_rules_in_ir(input.current_ir_mut(), dialect)
2183    else {
2184        return input
2185            .ir_rejected("typed IR transaction rejected the container static structural rewrite");
2186    };
2187    input.ir_mutation_result(
2188        mutation_count,
2189        "removed @container branches whose size condition is provably unsatisfiable",
2190    )
2191}
2192
2193fn run_native_css_static_eval_structural(
2194    mut input: TransformStructuralPassInputV0<'_>,
2195) -> TransformPassDispatchResultV0 {
2196    if input.dialect == StyleDialect::Css {
2197        let dialect = input.dialect;
2198        let Ok(mutation_count) =
2199            evaluate_native_css_static_values_in_ir(input.current_ir_mut(), dialect)
2200        else {
2201            return input.ir_rejected(
2202                "typed IR transaction rejected the native CSS static structural rewrite",
2203            );
2204        };
2205        input.ir_mutation_result(
2206            mutation_count,
2207            "folded fully static native CSS if() values and native CSS function calls while preserving runtime-dependent constructs",
2208        )
2209    } else {
2210        TransformPassDispatchResultV0::no_change(
2211            input.pass_id,
2212            input.input_byte_len,
2213            TransformNoChangeReasonV0::DialectNotApplicable,
2214            "preserved non-CSS dialect because native CSS static evaluation is CSS-only",
2215        )
2216    }
2217}
2218
2219fn run_dead_media_branch_removal_structural(
2220    mut input: TransformStructuralPassInputV0<'_>,
2221) -> TransformPassDispatchResultV0 {
2222    let dialect = input.dialect;
2223    let drop_dark_mode_media_queries = input.context.drop_dark_mode_media_queries;
2224    let Ok(mutation_count) = evaluate_dead_media_branch_rules_in_ir(
2225        input.current_ir_mut(),
2226        dialect,
2227        drop_dark_mode_media_queries,
2228    ) else {
2229        return input
2230            .ir_rejected("typed IR transaction rejected the dead media structural rewrite");
2231    };
2232    input.ir_mutation_result(
2233        mutation_count,
2234        "removed dead @media branches through the static cascade witness evaluator",
2235    )
2236}
2237
2238fn run_dead_supports_branch_removal_structural(
2239    mut input: TransformStructuralPassInputV0<'_>,
2240) -> TransformPassDispatchResultV0 {
2241    let dialect = input.dialect;
2242    let assumption = static_supports_assumption_from_context(input.context);
2243    let Ok(mutation_count) =
2244        evaluate_static_supports_rules_in_ir(input.current_ir_mut(), dialect, assumption)
2245    else {
2246        return input
2247            .ir_rejected("typed IR transaction rejected the dead supports structural rewrite");
2248    };
2249    input.ir_mutation_result(
2250        mutation_count,
2251        "removed dead @supports branches through the static cascade witness evaluator",
2252    )
2253}
2254
2255fn run_tree_shake_class_structural(
2256    mut input: TransformStructuralPassInputV0<'_>,
2257) -> TransformPassDispatchResultV0 {
2258    if let Some(reason) = input.precision_blocker() {
2259        return TransformPassDispatchResultV0::blocked(
2260            input.pass_id,
2261            input.input_byte_len,
2262            reason,
2263            "requires an explicit closed-world bundle before mutation",
2264        );
2265    }
2266    let Some(bundle) = input.closed_world_bundle() else {
2267        return TransformPassDispatchResultV0::blocked(
2268            input.pass_id,
2269            input.input_byte_len,
2270            TransformBlockedReasonV0::MissingPrecondition {
2271                precondition: TransformPreconditionV0::ClosedWorldBundle,
2272            },
2273            "requires an explicit closed-world bundle before mutation",
2274        );
2275    };
2276    let dialect = input.dialect;
2277    let reachable_class_names = input.module_reachable_class_names.map_or_else(
2278        || bundle.reachability().class_names().to_vec(),
2279        <[String]>::to_vec,
2280    );
2281    let Ok(removals) =
2282        tree_shake_css_class_rules_in_ir(input.current_ir_mut(), dialect, &reachable_class_names)
2283    else {
2284        return input
2285            .ir_rejected("typed IR transaction rejected the class tree-shake structural rewrite");
2286    };
2287    let mutation_count = removals.len();
2288    let mut result = input.ir_mutation_result(
2289        mutation_count,
2290        "removed unreachable class-owned selector rules under an explicit closed-world bundle",
2291    );
2292    result.semantic_removals = removals
2293        .into_iter()
2294        .map(|removal| removal.into_public(input.pass_id))
2295        .collect();
2296    result
2297}
2298
2299fn run_tree_shake_keyframes_structural(
2300    mut input: TransformStructuralPassInputV0<'_>,
2301) -> TransformPassDispatchResultV0 {
2302    if let Some(reason) = input.precision_blocker() {
2303        return TransformPassDispatchResultV0::blocked(
2304            input.pass_id,
2305            input.input_byte_len,
2306            reason,
2307            "requires an explicit closed-world bundle before mutation",
2308        );
2309    }
2310    let Some(bundle) = input.closed_world_bundle() else {
2311        return TransformPassDispatchResultV0::blocked(
2312            input.pass_id,
2313            input.input_byte_len,
2314            TransformBlockedReasonV0::MissingPrecondition {
2315                precondition: TransformPreconditionV0::ClosedWorldBundle,
2316            },
2317            "requires an explicit closed-world bundle before mutation",
2318        );
2319    };
2320    let dialect = input.dialect;
2321    let reachable_keyframe_names = input.module_qualified_symbols.map_or_else(
2322        || bundle.reachability().keyframe_names().to_vec(),
2323        |symbols| symbols.keyframe_names().to_vec(),
2324    );
2325    let reachable_class_names = input.module_reachable_class_names.map_or_else(
2326        || bundle.reachability().class_names().to_vec(),
2327        <[String]>::to_vec,
2328    );
2329    let Ok(removals) = tree_shake_css_keyframes_in_ir(
2330        input.current_ir_mut(),
2331        dialect,
2332        &reachable_keyframe_names,
2333        &reachable_class_names,
2334    ) else {
2335        return input.ir_rejected(
2336            "typed IR transaction rejected the keyframes tree-shake structural rewrite",
2337        );
2338    };
2339    let mutation_count = removals.len();
2340    let mut result = input.ir_mutation_result(
2341        mutation_count,
2342        "removed unreferenced @keyframes under an explicit closed-world bundle",
2343    );
2344    result.semantic_removals = removals
2345        .into_iter()
2346        .map(|removal| removal.into_public(input.pass_id))
2347        .collect();
2348    result
2349}
2350
2351fn run_tree_shake_value_structural(
2352    mut input: TransformStructuralPassInputV0<'_>,
2353) -> TransformPassDispatchResultV0 {
2354    if let Some(reason) = input.precision_blocker() {
2355        return TransformPassDispatchResultV0::blocked(
2356            input.pass_id,
2357            input.input_byte_len,
2358            reason,
2359            "requires an explicit closed-world bundle before mutation",
2360        );
2361    }
2362    let Some(bundle) = input.closed_world_bundle() else {
2363        return TransformPassDispatchResultV0::blocked(
2364            input.pass_id,
2365            input.input_byte_len,
2366            TransformBlockedReasonV0::MissingPrecondition {
2367                precondition: TransformPreconditionV0::ClosedWorldBundle,
2368            },
2369            "requires an explicit closed-world bundle before mutation",
2370        );
2371    };
2372    let dialect = input.dialect;
2373    let reachable_value_names = input.module_qualified_symbols.map_or_else(
2374        || bundle.reachability().value_names().to_vec(),
2375        |symbols| symbols.value_names().to_vec(),
2376    );
2377    let reachable_keyframe_names = input.module_qualified_symbols.map_or_else(
2378        || bundle.reachability().keyframe_names().to_vec(),
2379        |symbols| symbols.keyframe_names().to_vec(),
2380    );
2381    let reachable_class_names = input.module_reachable_class_names.map_or_else(
2382        || bundle.reachability().class_names().to_vec(),
2383        <[String]>::to_vec,
2384    );
2385    let Ok(removals) = tree_shake_css_modules_values_in_ir(
2386        input.current_ir_mut(),
2387        dialect,
2388        &reachable_value_names,
2389        &reachable_keyframe_names,
2390        &reachable_class_names,
2391    ) else {
2392        return input.ir_rejected(
2393            "typed IR transaction rejected the CSS Modules value tree-shake structural rewrite",
2394        );
2395    };
2396    let mutation_count = removals.len();
2397    let mut result = input.ir_mutation_result(
2398        mutation_count,
2399        "removed unreachable local CSS Modules @value declarations under an explicit closed-world bundle",
2400    );
2401    result.semantic_removals = removals
2402        .into_iter()
2403        .map(|removal| removal.into_public(input.pass_id))
2404        .collect();
2405    result
2406}
2407
2408fn run_tree_shake_custom_property_structural(
2409    mut input: TransformStructuralPassInputV0<'_>,
2410) -> TransformPassDispatchResultV0 {
2411    if let Some(reason) = input.precision_blocker() {
2412        return TransformPassDispatchResultV0::blocked(
2413            input.pass_id,
2414            input.input_byte_len,
2415            reason,
2416            "requires an explicit closed-world bundle before mutation",
2417        );
2418    }
2419    let Some(bundle) = input.closed_world_bundle() else {
2420        return TransformPassDispatchResultV0::blocked(
2421            input.pass_id,
2422            input.input_byte_len,
2423            TransformBlockedReasonV0::MissingPrecondition {
2424                precondition: TransformPreconditionV0::ClosedWorldBundle,
2425            },
2426            "requires an explicit closed-world bundle before mutation",
2427        );
2428    };
2429    let dialect = input.dialect;
2430    let reachable_custom_property_names = input.module_qualified_symbols.map_or_else(
2431        || bundle.reachability().custom_property_names().to_vec(),
2432        |symbols| symbols.custom_property_names().to_vec(),
2433    );
2434    let reachable_keyframe_names = input.module_qualified_symbols.map_or_else(
2435        || bundle.reachability().keyframe_names().to_vec(),
2436        |symbols| symbols.keyframe_names().to_vec(),
2437    );
2438    let reachable_class_names = input.module_reachable_class_names.map_or_else(
2439        || bundle.reachability().class_names().to_vec(),
2440        <[String]>::to_vec,
2441    );
2442    let Ok(removals) = tree_shake_css_custom_properties_in_ir(
2443        input.current_ir_mut(),
2444        dialect,
2445        &reachable_custom_property_names,
2446        &reachable_keyframe_names,
2447        &reachable_class_names,
2448    ) else {
2449        return input.ir_rejected(
2450            "typed IR transaction rejected the custom-property tree-shake structural rewrite",
2451        );
2452    };
2453    let mutation_count = removals.len();
2454    let mut result = input.ir_mutation_result(
2455        mutation_count,
2456        "removed unreachable custom-property declarations under an explicit closed-world bundle",
2457    );
2458    result.semantic_removals = removals
2459        .into_iter()
2460        .map(|removal| removal.into_public(input.pass_id))
2461        .collect();
2462    result
2463}
2464
2465fn run_empty_rule_removal_structural(
2466    mut input: TransformStructuralPassInputV0<'_>,
2467) -> TransformPassDispatchResultV0 {
2468    let dialect = input.dialect;
2469    let Ok(mutation_count) = remove_empty_css_rules_in_ir(input.current_ir_mut(), dialect) else {
2470        return input
2471            .ir_rejected("typed IR transaction rejected the empty-rule structural rewrite");
2472    };
2473    input.ir_mutation_result(
2474        mutation_count,
2475        "removed ordinary empty rules with no comments or at-rule semantics",
2476    )
2477}
2478
2479fn execute_transform_passes_on_source_with_active_lex_cache(
2480    source: &str,
2481    dialect: StyleDialect,
2482    requested: &[TransformPassKind],
2483    context: &TransformExecutionContextV0,
2484    explicit_closed_world_bundle: Option<&ClosedWorldBundleV0>,
2485    reachability_precision_ceiling: Option<FactPrecision>,
2486    runtime_policy: TransformExecutionRuntimePolicyV0<'_>,
2487) -> TransformExecutionSummaryV0 {
2488    reset_structural_ir_transaction_telemetry();
2489    let pass_plan = plan_transform_passes(requested);
2490    let pass_registry = default_transform_pass_registry();
2491    let stable_ir =
2492        build_stable_transform_ir_from_source(source, dialect, "omena-transform-passes.execution");
2493    let stable_ir_nodes = stable_ir.nodes;
2494    let mut coordinate_map = TransformSpanCoordinateMapV0::new(source.len());
2495    let requested_pass_ids = requested.iter().map(|pass| pass.id()).collect::<Vec<_>>();
2496    let ordered_pass_ids = pass_plan.ordered_pass_ids.clone();
2497    let mut document = TransformExecutionDocumentV0::new(source, dialect);
2498    let closed_world_bundle = explicit_closed_world_bundle;
2499    let module_qualified_symbols = runtime_policy.module_qualified_symbols;
2500    let module_reachable_class_names = module_qualified_symbols.map(|symbols| {
2501        let mut class_names = symbols.class_names().to_vec();
2502        class_names.extend(runtime_policy.retained_class_names.iter().cloned());
2503        class_names.sort();
2504        class_names.dedup();
2505        class_names
2506    });
2507    let mut decisions = Vec::new();
2508    let mut outcomes = Vec::new();
2509    let mut css_module_evaluation = None;
2510    let mut css_import_inlines = Vec::new();
2511    let mut css_module_composes_exports = Vec::new();
2512    let mut design_token_routes = Vec::new();
2513    let mut semantic_removals = Vec::new();
2514    let mut outcome_mutation_spans = Vec::new();
2515    let mut cascade_proof_obligations = Vec::new();
2516    let mut winner_equality_obligations = Vec::<TransformWinnerEqualityObligationV0>::new();
2517    let mut semantic_preservation_telemetry = TransformSemanticPreservationTelemetryV0::default();
2518    let strict_policy = runtime_policy.verification.strict_policy.as_ref();
2519    let semantic_trust_recording = runtime_policy.semantic_trust_recording;
2520    let strict_pass_ids = strict_policy
2521        .filter(|policy| policy.enforce_winner_equality)
2522        .map(|_| {
2523            strict_verification_build_profile()
2524                .pass_ids
2525                .into_iter()
2526                .collect::<BTreeSet<_>>()
2527        })
2528        .unwrap_or_default();
2529    let mut strict_policy_summary = strict_policy
2530        .map_or_else(TransformStrictPolicySummaryV0::default, |policy| {
2531            TransformStrictPolicySummaryV0::for_profile(policy.profile_id)
2532        });
2533    let mut closed_world_admission_summary = TransformClosedWorldAdmissionSummaryV0::default();
2534
2535    for (pass_index, pass_id) in ordered_pass_ids.iter().enumerate() {
2536        let should_maintain_document_lex_cache =
2537            next_document_lex_cache_consumer(ordered_pass_ids.as_slice(), pass_index)
2538                == Some(TransformTextLocalExecutionModeV0::FullDocument);
2539        let pass = transform_pass_kind_from_id(pass_id);
2540        let runtime_entry = pass
2541            .and_then(|kind| runtime_pass_entry_for_kind(kind, pass_registry.entries.as_slice()));
2542        let dispatch_kind = runtime_entry
2543            .as_ref()
2544            .map(|entry| entry.registry_entry.dispatch_kind);
2545        let input_byte_len = document.current_byte_len();
2546        let input_content_signature = transform_content_signature(document.current_css());
2547        let semantic_preservation_input_ir = pass
2548            .filter(|kind| semantic_preservation_applies(*kind))
2549            .map(|_| document.current_ir.clone());
2550        let semantic_preservation_projection = pass
2551            .filter(|kind| semantic_preservation_applies(*kind))
2552            .map(|kind| {
2553                SemanticObservationProjectionV0::for_pass_input(
2554                    kind,
2555                    &document.current_ir,
2556                    dialect,
2557                    closed_world_bundle,
2558                    module_qualified_symbols,
2559                    module_reachable_class_names.as_deref(),
2560                )
2561            })
2562            .unwrap_or_default();
2563        let mut textual_bridge = TransformTextualBridgeSnapshotV0::default();
2564        let pass_cascade_proof_obligations =
2565            if dispatch_kind == Some(TransformPassDispatchKindV0::StructuralIrTransaction) {
2566                collect_cascade_proof_obligations_for_ir_pass_input(
2567                    pass_id,
2568                    pass,
2569                    &document.current_ir,
2570                    context,
2571                    closed_world_bundle,
2572                )
2573            } else {
2574                let pass_input_css = textual_bridge.materialize_current_css(&document);
2575                collect_cascade_proof_obligations_for_pass_input(
2576                    pass_id,
2577                    pass,
2578                    pass_input_css,
2579                    dialect,
2580                    context,
2581                    closed_world_bundle,
2582                )
2583            };
2584        let discharge_evidence =
2585            flatten_discharge_evidence(pass, pass_cascade_proof_obligations.as_slice());
2586        let flatten_precondition_failure =
2587            flatten_discharge_precondition_failure(pass, pass_cascade_proof_obligations.as_slice());
2588        cascade_proof_obligations.extend(pass_cascade_proof_obligations);
2589        let closed_world_precondition_failure = pass
2590            .filter(|pass| transform_pass_requires_closed_world_bundle(*pass))
2591            .filter(|_| closed_world_bundle.is_none())
2592            .map(|pass| {
2593                let observed = classify_transform_reachability_precision(
2594                    context,
2595                    closed_world_bundle.is_some(),
2596                    reachability_precision_ceiling,
2597                );
2598                transform_structural_decision_policy(pass)
2599                    .and_then(|policy| policy.required_precision())
2600                    .filter(|required| !observed.satisfies(*required))
2601                    .map_or(
2602                        TransformBlockedReasonV0::MissingPrecondition {
2603                            precondition: TransformPreconditionV0::ClosedWorldBundle,
2604                        },
2605                        |required| TransformBlockedReasonV0::PrecisionBelowFloor {
2606                            required,
2607                            observed,
2608                        },
2609                    )
2610            });
2611        let strict_refusal_reasons = pass
2612            .filter(|pass| strict_pass_ids.contains(pass.id()))
2613            .map(|pass| strict_plan_refusal_reasons(pass, context))
2614            .unwrap_or_default();
2615        let fact_consuming_pass = pass.is_some_and(|pass| {
2616            matches!(
2617                transform_structural_decision_policy(pass).map(|policy| policy.class),
2618                Some(TransformStructuralDecisionClassV0::FactConsuming { .. })
2619            )
2620        });
2621        let closed_world_admission_reasons = pass
2622            .filter(|_| fact_consuming_pass)
2623            .and_then(|pass_kind| {
2624                closed_world_bundle.map(|bundle| {
2625                    let o3 = closed_world_admission_o3_reasons(bundle, module_qualified_symbols);
2626                    let mut reasons = closed_world_admission_o1_reasons(
2627                        runtime_policy.token_ownership_census,
2628                        runtime_policy
2629                            .token_ownership_module_instance
2630                            .or(o3.module_instance),
2631                        pass_kind,
2632                    );
2633                    reasons.extend(o3.reasons);
2634                    reasons.extend(closed_world_admission_o2_reasons(
2635                        bundle,
2636                        module_qualified_symbols,
2637                        module_reachable_class_names.as_deref(),
2638                        pass_kind,
2639                    ));
2640                    reasons
2641                })
2642            })
2643            .unwrap_or_default();
2644        if fact_consuming_pass
2645            && closed_world_bundle.is_some_and(|bundle| {
2646                bundle.composes_scan_state() == ClosedWorldComposesScanStateV0::SourceSetOpen
2647            })
2648        {
2649            closed_world_admission_summary.evidence_scope = Some("sourceSetOpen");
2650        }
2651        let strict_refused_before_dispatch = !strict_refusal_reasons.is_empty();
2652        let mut dispatch_result = if !strict_refusal_reasons.is_empty() {
2653            strict_policy_summary.record_refusal(*pass_id, strict_refusal_reasons.clone());
2654            TransformPassDispatchResultV0::blocked(
2655                pass_id,
2656                input_byte_len,
2657                TransformBlockedReasonV0::StrictVerification {
2658                    reasons: strict_refusal_reasons,
2659                },
2660                "strict verification evidence was unavailable before dispatch",
2661            )
2662        } else if !closed_world_admission_reasons.is_empty() {
2663            let module_instance = closed_world_admission_module_instance(
2664                closed_world_bundle,
2665                module_qualified_symbols,
2666            )
2667            .cloned();
2668            closed_world_admission_summary.record_refusal(
2669                *pass_id,
2670                module_instance,
2671                closed_world_admission_reasons.clone(),
2672            );
2673            TransformPassDispatchResultV0::blocked(
2674                pass_id,
2675                input_byte_len,
2676                TransformBlockedReasonV0::ClosedWorldAdmission {
2677                    reasons: closed_world_admission_reasons,
2678                },
2679                "closed-world admission evidence was incomplete before destructive dispatch",
2680            )
2681        } else if let Some(reason) = flatten_precondition_failure {
2682            TransformPassDispatchResultV0::blocked(
2683                pass_id,
2684                input_byte_len,
2685                reason,
2686                "fresh discharge ledger evidence required for flatten rewrite",
2687            )
2688        } else if let Some(reason) = closed_world_precondition_failure {
2689            TransformPassDispatchResultV0::blocked(
2690                pass_id,
2691                input_byte_len,
2692                reason,
2693                "closed-world bundle required before mutation",
2694            )
2695        } else {
2696            match runtime_entry.as_ref().map(|entry| entry.implementation) {
2697                Some(TransformRuntimePassImplementationV0::TextLocal(handler)) => {
2698                    dispatch_text_local_pass(
2699                        pass_id,
2700                        handler,
2701                        &document.current_ir,
2702                        dialect,
2703                        context,
2704                    )
2705                }
2706                Some(TransformRuntimePassImplementationV0::ModuleEvaluation(pass)) => {
2707                    let pass_input_css = textual_bridge.materialize_current_css(&document);
2708                    dispatch_module_evaluation_pass(pass_id, pass, pass_input_css, dialect, context)
2709                }
2710                Some(TransformRuntimePassImplementationV0::Structural(handler)) => {
2711                    if should_maintain_document_lex_cache {
2712                        textual_bridge.materialize_current_css(&document);
2713                    }
2714                    dispatch_structural_pass(
2715                        pass_id,
2716                        handler,
2717                        document.current_ir_mut(),
2718                        TransformStructuralDispatchContextV0 {
2719                            dialect,
2720                            execution: context,
2721                            closed_world_bundle,
2722                            module_qualified_symbols,
2723                            module_reachable_class_names: module_reachable_class_names.as_deref(),
2724                            reachability_precision_ceiling,
2725                        },
2726                    )
2727                }
2728                Some(TransformRuntimePassImplementationV0::Emission(pass)) => {
2729                    dispatch_emission_pass(pass_id, pass, input_byte_len)
2730                }
2731                None => None,
2732            }
2733            .unwrap_or_else(|| {
2734                TransformPassDispatchResultV0::blocked(
2735                    pass_id,
2736                    input_byte_len,
2737                    TransformBlockedReasonV0::PassImplementation,
2738                    "unknown pass id in execution plan",
2739                )
2740            })
2741        };
2742
2743        let semantic_mutation_spans = dispatch_result.semantic_mutation_spans();
2744        let mut winner_equality_tier = None;
2745        let blocked_before_semantic_observation = matches!(
2746            dispatch_result.decision,
2747            TransformDecisionDraftV0::Blocked { .. }
2748        );
2749        if !strict_refused_before_dispatch
2750            && blocked_before_semantic_observation
2751            && pass.is_some_and(semantic_preservation_applies)
2752        {
2753            semantic_preservation_telemetry.record_blocked_before_observation();
2754        } else if !strict_refused_before_dispatch
2755            && let (Some(pass_kind), Some(input_ir)) =
2756                (pass, semantic_preservation_input_ir.as_ref())
2757        {
2758            let enforcement_context = TransformSemanticPreservationEnforcementContextV0 {
2759                closed_world_bundle,
2760                module_qualified_symbols,
2761                module_reachable_class_names: module_reachable_class_names.as_deref(),
2762                projection: &semantic_preservation_projection,
2763                mutation_spans: semantic_mutation_spans.as_slice(),
2764                cascade_environment: context.cascade_environment.as_ref(),
2765                observe_pending_textual_output: strict_pass_ids.contains(pass_kind.id()),
2766            };
2767            let (enforced, winner_equality) = enforce_semantic_preservation_for_dispatch_result(
2768                pass_kind,
2769                input_ir,
2770                &mut document,
2771                dispatch_result,
2772                &mut semantic_preservation_telemetry,
2773                enforcement_context,
2774            );
2775            dispatch_result = enforced;
2776            if let Some(evaluation) = winner_equality {
2777                let strict_rollback_reasons = if strict_pass_ids.contains(pass_kind.id()) {
2778                    strict_rollback_reasons(pass_kind, &evaluation)
2779                } else {
2780                    Vec::new()
2781                };
2782                winner_equality_tier = Some(evaluation.tier.clone());
2783                if semantic_trust_recording.records() {
2784                    winner_equality_obligations.extend(evaluation.obligations.clone());
2785                }
2786                if !strict_rollback_reasons.is_empty() {
2787                    dispatch_result = apply_strict_winner_rollback(
2788                        pass_kind,
2789                        input_ir,
2790                        &mut document,
2791                        strict_rollback_reasons,
2792                        dispatch_result,
2793                        &mut strict_policy_summary,
2794                    );
2795                }
2796            }
2797        }
2798        let preserved_output_signature = transform_content_signature(document.current_css());
2799
2800        let TransformPassDispatchResultV0 {
2801            next_textual_css,
2802            document_ir_updated,
2803            decision: decision_draft,
2804            css_module_evaluation: dispatched_css_module_evaluation,
2805            css_import_inlines: dispatched_css_import_inlines,
2806            css_module_composes_exports: dispatched_css_module_composes_exports,
2807            design_token_routes: dispatched_design_token_routes,
2808            semantic_removals: dispatched_semantic_removals,
2809            provenance_mutation_spans,
2810        } = dispatch_result;
2811        let decision = decision_draft.finalize(
2812            input_content_signature,
2813            preserved_output_signature,
2814            discharge_evidence,
2815            semantic_trust_recording.decision_tier(pass, winner_equality_tier),
2816        );
2817        let outcome = decision.compatibility_outcome().clone();
2818        if let Some(evaluation) = dispatched_css_module_evaluation {
2819            css_module_evaluation = Some(evaluation);
2820        }
2821        if !dispatched_css_import_inlines.is_empty() {
2822            css_import_inlines = dispatched_css_import_inlines;
2823        }
2824        if !dispatched_css_module_composes_exports.is_empty() {
2825            css_module_composes_exports = dispatched_css_module_composes_exports;
2826        }
2827        if !dispatched_design_token_routes.is_empty() {
2828            design_token_routes = dispatched_design_token_routes;
2829        }
2830        semantic_removals.extend(dispatched_semantic_removals);
2831        if document_ir_updated {
2832            let output_byte_len = document.current_byte_len();
2833            let mut mutation_spans = match provenance_mutation_spans {
2834                Some(mutation_spans) => mutation_spans,
2835                None => {
2836                    vec![TransformProvenanceMutationSpanV0 {
2837                        source_span_start: 0,
2838                        source_span_end: input_byte_len,
2839                        generated_span_start: 0,
2840                        generated_span_end: output_byte_len,
2841                        node_key: None,
2842                    }]
2843                }
2844            };
2845            stamp_mutation_span_node_keys(
2846                mutation_spans.as_mut_slice(),
2847                &coordinate_map,
2848                stable_ir_nodes.as_slice(),
2849            );
2850            if should_maintain_document_lex_cache {
2851                let next_css = document.current_css().to_string();
2852                let pass_input_css = textual_bridge.materialize_current_css(&document);
2853                super::lex_cache::update_cached_lex_from_splice(
2854                    pass_input_css,
2855                    &next_css,
2856                    dialect,
2857                    mutation_spans.as_slice(),
2858                );
2859            }
2860            coordinate_map.apply_mutation_spans(mutation_spans.as_slice());
2861            outcome_mutation_spans.push(mutation_spans);
2862        } else {
2863            match next_textual_css {
2864                Some(next_css) => {
2865                    let mut mutation_spans = match provenance_mutation_spans {
2866                        Some(mutation_spans) => mutation_spans,
2867                        _ => {
2868                            let pass_input_css = textual_bridge.materialize_current_css(&document);
2869                            derive_transform_mutation_spans(pass_input_css, &next_css)
2870                        }
2871                    };
2872                    stamp_mutation_span_node_keys(
2873                        mutation_spans.as_mut_slice(),
2874                        &coordinate_map,
2875                        stable_ir_nodes.as_slice(),
2876                    );
2877                    if should_maintain_document_lex_cache {
2878                        let pass_input_css = textual_bridge.materialize_current_css(&document);
2879                        super::lex_cache::update_cached_lex_from_splice(
2880                            pass_input_css,
2881                            &next_css,
2882                            dialect,
2883                            mutation_spans.as_slice(),
2884                        );
2885                    }
2886                    coordinate_map.apply_mutation_spans(mutation_spans.as_slice());
2887                    outcome_mutation_spans.push(mutation_spans);
2888                    document.replace_with_css(next_css);
2889                }
2890                None => {
2891                    outcome_mutation_spans.push(Vec::new());
2892                }
2893            }
2894        }
2895        decisions.push(decision);
2896        outcomes.push(outcome);
2897    }
2898
2899    let executed_pass_ids = outcomes
2900        .iter()
2901        .filter(|outcome| outcome.status != TransformPassRuntimeStatus::PlannedOnly)
2902        .map(|outcome| outcome.pass_id)
2903        .collect::<Vec<_>>();
2904    let planned_only_pass_ids = outcomes
2905        .iter()
2906        .filter(|outcome| outcome.status == TransformPassRuntimeStatus::PlannedOnly)
2907        .map(|outcome| outcome.pass_id)
2908        .collect::<Vec<_>>();
2909    let mutation_count = outcomes
2910        .iter()
2911        .map(|outcome| outcome.mutation_count)
2912        .sum::<usize>();
2913    let provenance_preserved = outcomes.iter().all(|outcome| outcome.provenance_preserved);
2914    let provenance_derivation_forest =
2915        provenance_derivation_forest_from_outcomes(&outcomes, &outcome_mutation_spans);
2916    let cascade_proof_obligations = summarize_cascade_proof_obligations(cascade_proof_obligations);
2917    let discharge_ledger_telemetry =
2918        summarize_discharge_ledger_telemetry(&cascade_proof_obligations);
2919    let structural_ir_transaction_telemetry = structural_ir_transaction_telemetry_snapshot();
2920    let output_byte_len = document.current_byte_len();
2921    let output_css = document.output_css();
2922    let module_qualified_shake = module_qualified_symbols.map(|symbols| {
2923        let tree_shake_pass_ids = [
2924            TransformPassKind::TreeShakeClass.id(),
2925            TransformPassKind::TreeShakeKeyframes.id(),
2926            TransformPassKind::TreeShakeValue.id(),
2927            TransformPassKind::TreeShakeCustomProperty.id(),
2928        ];
2929        TransformModuleQualifiedShakeSummaryV0 {
2930            module_instance: symbols.module_instance().clone(),
2931            removed_count: semantic_removals
2932                .iter()
2933                .filter(|removal| tree_shake_pass_ids.contains(&removal.pass_id))
2934                .count(),
2935        }
2936    });
2937
2938    TransformExecutionSummaryV0 {
2939        schema_version: "0",
2940        product: "omena-transform-passes.execution",
2941        input_byte_len: source.len(),
2942        output_byte_len,
2943        requested_pass_ids,
2944        ordered_pass_ids,
2945        executed_pass_ids,
2946        planned_only_pass_ids,
2947        mutation_count,
2948        provenance_preserved,
2949        output_css,
2950        css_module_evaluation,
2951        css_import_inlines,
2952        css_module_composes_exports,
2953        design_token_routes,
2954        semantic_removals,
2955        module_qualified_shake,
2956        cascade_proof_obligations,
2957        winner_equality_obligations,
2958        provenance_derivation_forest,
2959        structural_ir_transaction_telemetry,
2960        semantic_preservation_telemetry,
2961        discharge_ledger_telemetry,
2962        strict_policy: strict_policy_summary,
2963        closed_world_admission: closed_world_admission_summary,
2964        decisions,
2965        outcomes,
2966        pass_plan,
2967    }
2968}
2969
2970fn closed_world_admission_module_instance<'a>(
2971    closed_world_bundle: Option<&'a ClosedWorldBundleV0>,
2972    module_qualified_symbols: Option<&'a ModuleQualifiedSymbolSetV0>,
2973) -> Option<&'a ModuleInstanceKeyV0> {
2974    module_qualified_symbols
2975        .map(ModuleQualifiedSymbolSetV0::module_instance)
2976        .or_else(|| {
2977            closed_world_bundle.and_then(|bundle| match bundle.linked_modules() {
2978                [module_instance] => Some(module_instance),
2979                _ => None,
2980            })
2981        })
2982}
2983
2984fn closed_world_admission_o1_reasons(
2985    census: Option<&CssModuleTokenOwnershipCensusV0>,
2986    module_instance: Option<&ModuleInstanceKeyV0>,
2987    pass_kind: TransformPassKind,
2988) -> Vec<TransformStrictPolicyReasonV0> {
2989    if pass_kind != TransformPassKind::TreeShakeClass {
2990        return Vec::new();
2991    }
2992    let Some(census) = census else {
2993        // The compatibility executor does not claim an emitted-token closure. Product bundle
2994        // emission supplies an explicit census (including an unavailable census on analysis
2995        // failure), so absence here is not silently converted into a complete empty observation.
2996        return Vec::new();
2997    };
2998
2999    let collision_reasons = census
3000        .module_token_collisions
3001        .iter()
3002        .filter(|collision| {
3003            module_instance
3004                .is_none_or(|module_instance| collision.module_instances.contains(module_instance))
3005        })
3006        .map(
3007            |collision| TransformStrictPolicyReasonV0::OwnershipNotSeparable {
3008                token: collision.emitted_token.clone(),
3009                module_paths: collision.module_paths.clone(),
3010            },
3011        );
3012    let unattributed_reasons = census.unattributed_emitted_tokens.iter().map(|token| {
3013        TransformStrictPolicyReasonV0::OwnershipNotSeparable {
3014            token: token.clone(),
3015            module_paths: Vec::new(),
3016        }
3017    });
3018    let interface_mismatch_reasons = census
3019        .interface_mismatches
3020        .iter()
3021        .filter(|mismatch| {
3022            module_instance
3023                .is_none_or(|module_instance| &mismatch.module_instance == module_instance)
3024        })
3025        .map(
3026            |mismatch| TransformStrictPolicyReasonV0::ClosedWorldEvidenceIncomplete {
3027                missing: vec![format!(
3028                    "cssModuleInterfaceToken:{}",
3029                    mismatch.promised_token
3030                )],
3031            },
3032        );
3033    let mut reasons = collision_reasons
3034        .chain(unattributed_reasons)
3035        .chain(interface_mismatch_reasons)
3036        .collect::<Vec<_>>();
3037    if !census.complete && reasons.is_empty() {
3038        reasons.push(
3039            TransformStrictPolicyReasonV0::ClosedWorldEvidenceIncomplete {
3040                missing: if census.unavailable_reasons.is_empty() {
3041                    vec!["cssModuleTokenOwnershipCensus".to_string()]
3042                } else {
3043                    census.unavailable_reasons.clone()
3044                },
3045            },
3046        );
3047    }
3048    if reasons.is_empty()
3049        && checked_token_ownership_admission_v0(census, module_instance, pass_kind).is_none()
3050    {
3051        reasons.push(
3052            TransformStrictPolicyReasonV0::ClosedWorldEvidenceIncomplete {
3053                missing: vec![format!("proofKernelToken:{}", pass_kind.id())],
3054            },
3055        );
3056    }
3057    reasons
3058}
3059
3060fn checked_token_ownership_admission_v0(
3061    census: &CssModuleTokenOwnershipCensusV0,
3062    module_instance: Option<&ModuleInstanceKeyV0>,
3063    pass_kind: TransformPassKind,
3064) -> Option<omena_cascade_proof::RewriteIssuanceTokenV0> {
3065    let module_label = module_instance.map_or_else(
3066        || "workspace".to_owned(),
3067        |module_instance| {
3068            format!(
3069                "{}#{}",
3070                module_instance.module().as_str(),
3071                module_instance.configuration().as_str()
3072            )
3073        },
3074    );
3075    let before = RewriteTermV0::apply(
3076        "closedWorldAdmissionRequested",
3077        vec![
3078            RewriteTermV0::atom(pass_kind.id()),
3079            RewriteTermV0::atom(module_label.clone()),
3080        ],
3081    );
3082    let after = RewriteTermV0::apply(
3083        "closedWorldAdmissionGranted",
3084        vec![
3085            RewriteTermV0::atom(pass_kind.id()),
3086            RewriteTermV0::atom(module_label.clone()),
3087        ],
3088    );
3089    let catalog = RewriteRuleCatalogV0 {
3090        schema_version: REWRITE_RULE_CATALOG_SCHEMA_VERSION_V0.to_owned(),
3091        operators: vec![
3092            RewriteOperatorV0 {
3093                operator: "closedWorldAdmissionRequested".to_owned(),
3094                arity: 2,
3095            },
3096            RewriteOperatorV0 {
3097                operator: "closedWorldAdmissionGranted".to_owned(),
3098                arity: 2,
3099            },
3100        ],
3101        rules: vec![RewriteRuleV0 {
3102            rule_id: "closed-world-token-ownership-admission-v0".to_owned(),
3103            before_pattern: RewritePatternV0::apply(
3104                "closedWorldAdmissionRequested",
3105                vec![
3106                    RewritePatternV0::variable("pass"),
3107                    RewritePatternV0::variable("module"),
3108                ],
3109            ),
3110            after_pattern: RewritePatternV0::apply(
3111                "closedWorldAdmissionGranted",
3112                vec![
3113                    RewritePatternV0::variable("pass"),
3114                    RewritePatternV0::variable("module"),
3115                ],
3116            ),
3117            side_condition_kind: RewriteSideConditionKindV0::TokenOwnershipSeparability,
3118        }],
3119    };
3120    let mut modeled_preimage_count = 0;
3121    let ownerships = census
3122        .token_ownerships
3123        .iter()
3124        .filter_map(|ownership| {
3125            let module_paths = if let Some(module_instance) = module_instance {
3126                ownership
3127                    .module_instances
3128                    .iter()
3129                    .zip(&ownership.module_paths)
3130                    .filter_map(|(candidate, path)| {
3131                        (candidate == module_instance).then_some(path.clone())
3132                    })
3133                    .collect::<Vec<_>>()
3134            } else {
3135                ownership.module_paths.clone()
3136            };
3137            if module_instance.is_some() && module_paths.is_empty() {
3138                return None;
3139            }
3140            modeled_preimage_count += ownership.original_names.len();
3141            Some(TokenOwnershipCertEntryV0 {
3142                emitted_token: ownership.emitted_token.clone(),
3143                module_paths,
3144            })
3145        })
3146        .collect::<Vec<_>>();
3147    let certificate = RewriteCertificateEnvelopeV0 {
3148        schema_version: REWRITE_CERTIFICATE_SCHEMA_VERSION_V0.to_owned(),
3149        max_depth: 1,
3150        max_nodes: 1,
3151        certificate: RewriteCertificateV0::Rewrite {
3152            rule_id: "closed-world-token-ownership-admission-v0".to_owned(),
3153            substitution: vec![
3154                RewriteSubstitutionEntryV0 {
3155                    variable: "module".to_owned(),
3156                    term: RewriteTermV0::atom(module_label),
3157                },
3158                RewriteSubstitutionEntryV0 {
3159                    variable: "pass".to_owned(),
3160                    term: RewriteTermV0::atom(pass_kind.id()),
3161                },
3162            ],
3163            side_condition: SideConditionCertV0::TokenOwnershipSeparability {
3164                certificate: TokenOwnershipSeparabilityCertV0 {
3165                    complete: census.complete && census.unavailable_reasons.is_empty(),
3166                    modeled_preimage_count,
3167                    emitted_token_count: ownerships.len()
3168                        + census.unattributed_emitted_tokens.len(),
3169                    ownerships,
3170                    unattributed_emitted_token_count: census.unattributed_emitted_tokens.len(),
3171                    interface_mismatch_count: census
3172                        .interface_mismatches
3173                        .iter()
3174                        .filter(|mismatch| {
3175                            module_instance.is_none_or(|module_instance| {
3176                                &mismatch.module_instance == module_instance
3177                            })
3178                        })
3179                        .count(),
3180                },
3181            },
3182        },
3183    };
3184    check_rewrite_certificate_v0(
3185        &before,
3186        &after,
3187        &catalog,
3188        &certificate,
3189        &CanonicalRewriteAssumptionsV0::default(),
3190    )
3191    .ok()
3192    .filter(|token| {
3193        token.matches_endpoints_v0(&before, &after) && token.matches_catalog_v0(&catalog)
3194    })
3195}
3196
3197struct ClosedWorldAdmissionO3V0<'a> {
3198    reasons: Vec<TransformStrictPolicyReasonV0>,
3199    module_instance: Option<&'a ModuleInstanceKeyV0>,
3200}
3201
3202fn closed_world_admission_o3_reasons<'a>(
3203    bundle: &'a ClosedWorldBundleV0,
3204    module_qualified_symbols: Option<&'a ModuleQualifiedSymbolSetV0>,
3205) -> ClosedWorldAdmissionO3V0<'a> {
3206    // Structural entailment: planning and admission borrow the same immutable sealed bundle, so
3207    // recomputing its ownership digest here cannot detect drift. The transform execution planner
3208    // owns re-entry if planning and execution ever receive independently materialized evidence.
3209    // The digest accessor remains available for that boundary, but calling it twice inside this
3210    // function would still compare two projections of one object. There is no plan token,
3211    // snapshot, mutation epoch, or independently owned bundle between the two reads. Keeping such
3212    // a limb would therefore advertise temporal protection without observing a divergence. A
3213    // future split planner must cross that temporal boundary explicitly and carry the captured
3214    // value alongside the independently materialized bundle before admission can compare the two
3215    // witnesses.
3216    let module_instance =
3217        closed_world_admission_module_instance(Some(bundle), module_qualified_symbols);
3218    if module_instance.is_some_and(|module_instance| {
3219        !bundle
3220            .linked_modules()
3221            .iter()
3222            .any(|linked| linked == module_instance)
3223    }) {
3224        return ClosedWorldAdmissionO3V0 {
3225            reasons: Vec::new(),
3226            module_instance,
3227        };
3228    }
3229    let mut missing = Vec::new();
3230    let checked_modules = module_instance.map_or_else(
3231        || bundle.linked_modules(),
3232        |module_instance| std::slice::from_ref(module_instance),
3233    );
3234    if checked_modules.is_empty() {
3235        missing.push("moduleInstance".to_string());
3236    }
3237    if checked_modules.iter().any(|module_instance| {
3238        !bundle.interface_hashes().entries().iter().any(|entry| {
3239            &entry.module_instance == module_instance
3240                && matches!(
3241                    entry.availability,
3242                    ClosedWorldInterfaceHashAvailabilityV0::Known { .. }
3243                )
3244        })
3245    }) {
3246        missing.push("interfaceHash".to_string());
3247    }
3248    if checked_modules.iter().any(|module_instance| {
3249        bundle.module_reachability_evidence(module_instance)
3250            == ClosedWorldModuleReachabilityEvidenceV0::ModuleReachabilityInputAbsent
3251    }) {
3252        missing.push("moduleReachabilityInputAbsent".to_string());
3253    }
3254    if bundle
3255        .source_precision()
3256        .is_none_or(|precision| precision.unknown_source_count > 0)
3257    {
3258        missing.push("sourcePrecision".to_string());
3259    }
3260    let reasons = if missing.is_empty() {
3261        Vec::new()
3262    } else {
3263        vec![TransformStrictPolicyReasonV0::ClosedWorldEvidenceIncomplete { missing }]
3264    };
3265    ClosedWorldAdmissionO3V0 {
3266        reasons,
3267        module_instance,
3268    }
3269}
3270
3271fn closed_world_admission_o2_reasons(
3272    bundle: &ClosedWorldBundleV0,
3273    module_qualified_symbols: Option<&ModuleQualifiedSymbolSetV0>,
3274    module_reachable_class_names: Option<&[String]>,
3275    pass_kind: TransformPassKind,
3276) -> Vec<TransformStrictPolicyReasonV0> {
3277    if pass_kind != TransformPassKind::TreeShakeClass {
3278        return Vec::new();
3279    }
3280    let module_instance =
3281        closed_world_admission_module_instance(Some(bundle), module_qualified_symbols);
3282    let target_modules = module_instance.map_or_else(
3283        || bundle.linked_modules(),
3284        |module_instance| std::slice::from_ref(module_instance),
3285    );
3286    let checked = module_reachable_class_names
3287        .unwrap_or_else(|| bundle.reachability().class_names())
3288        .iter()
3289        .map(String::as_str)
3290        .collect::<BTreeSet<_>>();
3291    let inbound = bundle
3292        .composes_edges()
3293        .iter()
3294        .filter(|edge| target_modules.contains(&edge.to_module))
3295        .collect::<Vec<_>>();
3296    if inbound.iter().any(|edge| {
3297        bundle
3298            .composes_origin_symbol_is_reachable(&edge.from_module, edge.from_symbol.as_str())
3299            .is_none()
3300    }) {
3301        return vec![TransformStrictPolicyReasonV0::EvidenceUnavailable];
3302    }
3303    inbound
3304        .into_iter()
3305        .filter(|edge| {
3306            bundle.composes_origin_symbol_is_reachable(&edge.from_module, edge.from_symbol.as_str())
3307                == Some(true)
3308        })
3309        .filter(|edge| !checked.contains(edge.to_symbol.as_str()))
3310        .map(|edge| TransformStrictPolicyReasonV0::LivenessNotClosed {
3311            symbol: edge.to_symbol.clone(),
3312            from_module: edge.from_module.clone(),
3313            via_edge: "composes",
3314        })
3315        .collect()
3316}
3317
3318fn summarize_discharge_ledger_telemetry(
3319    report: &crate::TransformCascadeProofObligationReportV0,
3320) -> TransformDischargeLedgerTelemetryV0 {
3321    let lookups = report
3322        .obligations
3323        .iter()
3324        .filter_map(|obligation| obligation.discharge_ledger_lookup.as_ref())
3325        .collect::<Vec<_>>();
3326    let lookup_count = lookups.len() as u64;
3327    let matched_lookup_count = lookups
3328        .iter()
3329        .filter(|lookup| lookup.status == DischargeLedgerLookupStatusV0::Matched)
3330        .count() as u64;
3331    let accepted_stamp_count = lookups
3332        .iter()
3333        .filter(|lookup| lookup.can_apply_family_stamp())
3334        .count() as u64;
3335
3336    TransformDischargeLedgerTelemetryV0 {
3337        lookup_count,
3338        matched_lookup_count,
3339        accepted_stamp_count,
3340        blocked_lookup_count: lookup_count.saturating_sub(accepted_stamp_count),
3341    }
3342}
3343
3344fn strict_plan_refusal_reasons(
3345    pass: TransformPassKind,
3346    context: &TransformExecutionContextV0,
3347) -> Vec<TransformStrictPolicyReasonV0> {
3348    let driven_axes = driven_transform_axes().into_iter().collect::<BTreeSet<_>>();
3349    let required_axes = strict_required_winner_axes(pass);
3350    let mut reasons = required_axes
3351        .iter()
3352        .filter(|axis| !driven_axes.contains(axis))
3353        .copied()
3354        .map(|axis| TransformStrictPolicyReasonV0::RequiredAxisUnavailable { axis })
3355        .collect::<Vec<_>>();
3356    if required_axes.contains(&TransformWinnerEqualityAxisV0::CascadeLevel)
3357        && context.cascade_environment.is_none()
3358    {
3359        reasons.push(TransformStrictPolicyReasonV0::CascadeEnvironmentUnavailable);
3360    }
3361    reasons
3362}
3363
3364fn strict_rollback_reasons(
3365    pass: TransformPassKind,
3366    evaluation: &TransformWinnerEqualityEvaluationV0,
3367) -> Vec<TransformStrictPolicyReasonV0> {
3368    let required_axes = strict_required_winner_axes(pass)
3369        .into_iter()
3370        .collect::<BTreeSet<_>>();
3371    let mut changed_axes = BTreeSet::new();
3372    let mut absences = Vec::new();
3373    for obligation in &evaluation.obligations {
3374        match &obligation.observation {
3375            TransformWinnerEqualityObservationV0::ObservedEqual { .. }
3376            | TransformWinnerEqualityObservationV0::ObservedGuardedEqual { .. } => {}
3377            TransformWinnerEqualityObservationV0::ObservedDifferent { axes, .. } => {
3378                changed_axes.extend(axes.iter().copied());
3379            }
3380            TransformWinnerEqualityObservationV0::Absent { reasons } => {
3381                for reason in reasons {
3382                    if required_axes.contains(&reason.axis) && !absences.contains(reason) {
3383                        absences.push(reason.clone());
3384                    }
3385                }
3386            }
3387        }
3388    }
3389    for reason in &evaluation.unresolved_reasons {
3390        if required_axes.contains(&reason.axis) && !absences.contains(reason) {
3391            absences.push(reason.clone());
3392        }
3393    }
3394
3395    let mut reasons = Vec::new();
3396    if !changed_axes.is_empty() {
3397        reasons.push(TransformStrictPolicyReasonV0::WinnerChanged {
3398            axes: changed_axes.into_iter().collect(),
3399        });
3400    }
3401    if !absences.is_empty() {
3402        reasons.push(TransformStrictPolicyReasonV0::ObservationUnavailable { reasons: absences });
3403    }
3404    reasons
3405}
3406
3407fn apply_strict_winner_rollback(
3408    pass: TransformPassKind,
3409    input_ir: &TransformIrV0,
3410    document: &mut TransformExecutionDocumentV0,
3411    reasons: Vec<TransformStrictPolicyReasonV0>,
3412    dispatch_result: TransformPassDispatchResultV0,
3413    summary: &mut TransformStrictPolicySummaryV0,
3414) -> TransformPassDispatchResultV0 {
3415    if reasons.is_empty() {
3416        return dispatch_result;
3417    }
3418
3419    document.current_ir = input_ir.clone();
3420    summary.record_rollback(pass.id(), reasons.clone());
3421    TransformPassDispatchResultV0::rejected(
3422        pass.id(),
3423        input_ir.source_text().len(),
3424        TransformRejectionReasonV0::StrictVerification { reasons },
3425        "strict verification rolled back a winner-sensitive rewrite",
3426    )
3427}
3428
3429#[derive(Clone, Copy)]
3430struct TransformSemanticPreservationEnforcementContextV0<'a> {
3431    closed_world_bundle: Option<&'a ClosedWorldBundleV0>,
3432    module_qualified_symbols: Option<&'a ModuleQualifiedSymbolSetV0>,
3433    module_reachable_class_names: Option<&'a [String]>,
3434    projection: &'a SemanticObservationProjectionV0,
3435    mutation_spans: &'a [TransformProvenanceMutationSpanV0],
3436    cascade_environment: Option<&'a crate::model::TransformCascadeEnvironmentV0>,
3437    observe_pending_textual_output: bool,
3438}
3439
3440fn enforce_semantic_preservation_for_dispatch_result(
3441    pass: TransformPassKind,
3442    input_ir: &TransformIrV0,
3443    document: &mut TransformExecutionDocumentV0,
3444    dispatch_result: TransformPassDispatchResultV0,
3445    telemetry: &mut TransformSemanticPreservationTelemetryV0,
3446    context: TransformSemanticPreservationEnforcementContextV0<'_>,
3447) -> (
3448    TransformPassDispatchResultV0,
3449    Option<TransformWinnerEqualityEvaluationV0>,
3450) {
3451    let input_scope = SemanticObservationScopeV0::for_pass(
3452        pass,
3453        document.dialect,
3454        context.closed_world_bundle,
3455        context.module_qualified_symbols,
3456        context.module_reachable_class_names,
3457        context.projection,
3458    );
3459    let output_scope = input_scope.without_ignored_source_ranges();
3460    let pass_id = pass.id();
3461    let output_ir;
3462    let observed_output_ir = if context.observe_pending_textual_output
3463        && let Some(next_css) = dispatch_result.next_textual_css.as_deref()
3464    {
3465        output_ir = lower_transform_ir_from_source(
3466            next_css,
3467            document.dialect,
3468            "omena-transform-passes.winner-equality.output",
3469        );
3470        &output_ir
3471    } else if semantic_preservation_needs_fresh_output_ir(pass) {
3472        output_ir = lower_transform_ir_from_source(
3473            document.current_ir.source_text(),
3474            document.dialect,
3475            "omena-transform-passes.semantic-preservation.output",
3476        );
3477        &output_ir
3478    } else {
3479        &document.current_ir
3480    };
3481    let decision = compare_semantic_observation_for_pass_with_scopes(
3482        pass_id,
3483        input_ir,
3484        observed_output_ir,
3485        input_scope,
3486        output_scope,
3487    );
3488    telemetry.record(&decision);
3489    if decision.preserved {
3490        let winner_equality = (!context.mutation_spans.is_empty()).then(|| {
3491            evaluate_transform_winner_equality(
3492                pass,
3493                input_ir,
3494                observed_output_ir,
3495                context.mutation_spans,
3496                document.dialect,
3497                TransformWinnerEqualityContextV0 {
3498                    input_scope,
3499                    output_scope,
3500                    cascade_environment: context.cascade_environment,
3501                },
3502            )
3503        });
3504        return (dispatch_result, winner_equality);
3505    }
3506
3507    document.current_ir = input_ir.clone();
3508    (
3509        TransformPassDispatchResultV0::rejected(
3510            pass_id,
3511            input_ir.source_text().len(),
3512            TransformRejectionReasonV0::SemanticPreservation,
3513            "semantic preservation check refused a structural rewrite",
3514        ),
3515        None,
3516    )
3517}
3518
3519fn semantic_preservation_needs_fresh_output_ir(pass: TransformPassKind) -> bool {
3520    matches!(
3521        pass,
3522        TransformPassKind::NestingUnwrap
3523            | TransformPassKind::ScopeFlatten
3524            | TransformPassKind::LayerFlatten
3525    )
3526}
3527
3528fn flatten_discharge_precondition_failure(
3529    pass: Option<TransformPassKind>,
3530    obligations: &[TransformCascadeProofObligationV0],
3531) -> Option<TransformBlockedReasonV0> {
3532    if !matches!(
3533        pass,
3534        Some(TransformPassKind::ScopeFlatten | TransformPassKind::LayerFlatten)
3535    ) {
3536        return None;
3537    }
3538    if obligations.is_empty() {
3539        return Some(TransformBlockedReasonV0::DischargeMissing {
3540            lookup_status: None,
3541            verdict: None,
3542        });
3543    }
3544    let failed_obligation = obligations
3545        .iter()
3546        .find(|obligation| !obligation.accepted || !has_matching_discharge_evidence(obligation));
3547    if let Some(obligation) = failed_obligation {
3548        return Some(TransformBlockedReasonV0::DischargeMissing {
3549            lookup_status: obligation
3550                .discharge_ledger_lookup
3551                .as_ref()
3552                .map(|lookup| lookup.status),
3553            verdict: obligation
3554                .discharge_ledger_lookup
3555                .as_ref()
3556                .and_then(|lookup| lookup.verdict),
3557        });
3558    }
3559
3560    None
3561}
3562
3563fn has_matching_discharge_evidence(obligation: &TransformCascadeProofObligationV0) -> bool {
3564    let Some(lookup) = obligation.discharge_ledger_lookup.as_ref() else {
3565        return false;
3566    };
3567    let Some(evidence) = obligation.discharge_evidence.as_ref() else {
3568        return false;
3569    };
3570    lookup.can_apply_family_stamp()
3571        && evidence.guarantee_family == GuaranteeFamilyV0::LedgerBackedObligationDischarge
3572        && evidence.ledger_cell_key == lookup.cell_key
3573        && lookup.boundedness_kind.as_deref() == Some(evidence.boundedness_kind.as_str())
3574}
3575
3576fn flatten_discharge_evidence(
3577    pass: Option<TransformPassKind>,
3578    obligations: &[TransformCascadeProofObligationV0],
3579) -> Vec<TransformDischargeEvidenceV0> {
3580    if !matches!(
3581        pass,
3582        Some(TransformPassKind::ScopeFlatten | TransformPassKind::LayerFlatten)
3583    ) {
3584        return Vec::new();
3585    }
3586    obligations
3587        .iter()
3588        .filter_map(|obligation| obligation.discharge_evidence.clone())
3589        .collect()
3590}
3591
3592fn transform_pass_may_consume_lex_cache(pass: TransformPassKind) -> bool {
3593    omena_transform_cst::transform_pass_class(pass) == TransformPassClassV0::TextLocal
3594}
3595
3596fn next_document_lex_cache_consumer(
3597    ordered_pass_ids: &[&'static str],
3598    pass_index: usize,
3599) -> Option<TransformTextLocalExecutionModeV0> {
3600    ordered_pass_ids
3601        .iter()
3602        .skip(pass_index + 1)
3603        .filter_map(|pass_id| transform_pass_kind_from_id(pass_id))
3604        .filter(|kind| transform_pass_may_consume_lex_cache(*kind))
3605        .find_map(text_local_execution_mode_for_kind)
3606}
3607
3608fn compose_ir_transaction_mutation_span_batches(
3609    input_byte_len: usize,
3610    output_byte_len: usize,
3611    span_batches: &[Vec<TransformProvenanceMutationSpanV0>],
3612) -> Option<Vec<TransformProvenanceMutationSpanV0>> {
3613    if span_batches.is_empty() {
3614        return None;
3615    }
3616
3617    let mut coordinate_map = TransformSpanCoordinateMapV0::new(input_byte_len);
3618    let mut composed_spans = Vec::new();
3619    for batch in span_batches {
3620        if batch.is_empty() {
3621            return None;
3622        }
3623        remap_composed_generated_spans(composed_spans.as_mut_slice(), batch.as_slice())?;
3624        for span in batch {
3625            let (source_span_start, source_span_end) = coordinate_map
3626                .map_current_span_to_original(span.source_span_start, span.source_span_end)?;
3627            composed_spans.push(TransformProvenanceMutationSpanV0 {
3628                source_span_start,
3629                source_span_end,
3630                generated_span_start: span.generated_span_start,
3631                generated_span_end: span.generated_span_end,
3632                node_key: None,
3633            });
3634        }
3635        coordinate_map.apply_mutation_spans(batch.as_slice());
3636    }
3637
3638    if composed_spans
3639        .iter()
3640        .all(|span| span.generated_span_end <= output_byte_len)
3641    {
3642        Some(composed_spans)
3643    } else {
3644        None
3645    }
3646}
3647
3648fn remap_composed_generated_spans(
3649    composed_spans: &mut [TransformProvenanceMutationSpanV0],
3650    next_batch: &[TransformProvenanceMutationSpanV0],
3651) -> Option<()> {
3652    for span in composed_spans {
3653        span.generated_span_start =
3654            map_current_position_through_mutations(span.generated_span_start, next_batch)?;
3655        span.generated_span_end =
3656            map_current_position_through_mutations(span.generated_span_end, next_batch)?;
3657        if span.generated_span_start > span.generated_span_end {
3658            return None;
3659        }
3660    }
3661    Some(())
3662}
3663
3664struct TransformModuleEvaluationMaterializedOutput {
3665    css: String,
3666    detail: &'static str,
3667}
3668
3669fn materialize_transform_module_evaluation_output(
3670    input_css: &str,
3671    evaluation: &TransformModuleEvaluationV0,
3672    native_detail: &'static str,
3673    preserve_detail: &'static str,
3674) -> TransformModuleEvaluationMaterializedOutput {
3675    if !evaluation.may_consume_native_product_output() {
3676        return TransformModuleEvaluationMaterializedOutput {
3677            css: input_css.to_string(),
3678            detail: preserve_detail,
3679        };
3680    }
3681
3682    if let Some(native_edit_output) = evaluation.native_edit_output.as_ref() {
3683        if evaluation.native_output_matches_retained_oracle(native_edit_output) {
3684            return TransformModuleEvaluationMaterializedOutput {
3685                css: native_edit_output.clone(),
3686                detail: native_detail,
3687            };
3688        }
3689        return TransformModuleEvaluationMaterializedOutput {
3690            css: input_css.to_string(),
3691            detail: preserve_detail,
3692        };
3693    }
3694
3695    if let Some(native_css) =
3696        apply_transform_module_evaluation_native_edits(input_css, &evaluation.native_edits)
3697        && native_css == evaluation.evaluated_css
3698        && evaluation.native_output_matches_retained_oracle(native_css.as_str())
3699    {
3700        return TransformModuleEvaluationMaterializedOutput {
3701            css: native_css,
3702            detail: native_detail,
3703        };
3704    }
3705
3706    TransformModuleEvaluationMaterializedOutput {
3707        css: input_css.to_string(),
3708        detail: preserve_detail,
3709    }
3710}
3711
3712fn apply_transform_module_evaluation_native_edits(
3713    input_css: &str,
3714    native_edits: &[TransformModuleEvaluationNativeEditV0],
3715) -> Option<String> {
3716    if native_edits.is_empty() {
3717        return None;
3718    }
3719
3720    let mut edits = native_edits.to_vec();
3721    edits.sort_by_key(|edit| edit.start);
3722
3723    let mut previous_end = 0usize;
3724    for edit in &edits {
3725        if edit.start < previous_end
3726            || edit.start > edit.end
3727            || edit.end > input_css.len()
3728            || !input_css.is_char_boundary(edit.start)
3729            || !input_css.is_char_boundary(edit.end)
3730        {
3731            return None;
3732        }
3733        previous_end = edit.end;
3734    }
3735
3736    let mut output = input_css.to_string();
3737    for edit in edits.iter().rev() {
3738        output.replace_range(edit.start..edit.end, edit.replacement.as_str());
3739    }
3740    Some(output)
3741}
3742
3743#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3744struct TransformSpanMapSegmentV0 {
3745    current_start: usize,
3746    current_end: usize,
3747    original_start: usize,
3748}
3749
3750#[derive(Debug, Clone, PartialEq, Eq)]
3751struct TransformSpanCoordinateMapV0 {
3752    segments: Vec<TransformSpanMapSegmentV0>,
3753}
3754
3755impl TransformSpanCoordinateMapV0 {
3756    fn new(source_len: usize) -> Self {
3757        Self {
3758            segments: vec![TransformSpanMapSegmentV0 {
3759                current_start: 0,
3760                current_end: source_len,
3761                original_start: 0,
3762            }],
3763        }
3764    }
3765
3766    fn map_current_span_to_original(
3767        &self,
3768        current_start: usize,
3769        current_end: usize,
3770    ) -> Option<(usize, usize)> {
3771        let segment = self.segments.iter().find(|segment| {
3772            segment.current_start <= current_start && current_end <= segment.current_end
3773        })?;
3774        let original_start = segment.original_start + current_start - segment.current_start;
3775        let original_end = original_start + current_end.saturating_sub(current_start);
3776        Some((original_start, original_end))
3777    }
3778
3779    fn apply_mutation_spans(&mut self, mutation_spans: &[TransformProvenanceMutationSpanV0]) {
3780        if mutation_spans.is_empty() {
3781            return;
3782        }
3783
3784        let mut sorted_spans = mutation_spans.to_vec();
3785        sorted_spans.sort_by(|left, right| {
3786            left.source_span_start
3787                .cmp(&right.source_span_start)
3788                .then_with(|| left.source_span_end.cmp(&right.source_span_end))
3789        });
3790
3791        let mut next_segments = Vec::new();
3792        for segment in &self.segments {
3793            let mut cursor = segment.current_start;
3794            for span in &sorted_spans {
3795                if span.source_span_end <= cursor {
3796                    continue;
3797                }
3798                if span.source_span_start >= segment.current_end {
3799                    break;
3800                }
3801                let unchanged_end = span.source_span_start.min(segment.current_end);
3802                self.push_mapped_piece(
3803                    segment,
3804                    cursor,
3805                    unchanged_end,
3806                    &sorted_spans,
3807                    &mut next_segments,
3808                );
3809                cursor = cursor.max(span.source_span_end.min(segment.current_end));
3810            }
3811            self.push_mapped_piece(
3812                segment,
3813                cursor,
3814                segment.current_end,
3815                &sorted_spans,
3816                &mut next_segments,
3817            );
3818        }
3819        self.segments = next_segments;
3820    }
3821
3822    fn push_mapped_piece(
3823        &self,
3824        segment: &TransformSpanMapSegmentV0,
3825        current_start: usize,
3826        current_end: usize,
3827        mutation_spans: &[TransformProvenanceMutationSpanV0],
3828        next_segments: &mut Vec<TransformSpanMapSegmentV0>,
3829    ) {
3830        if current_start >= current_end {
3831            return;
3832        }
3833        let Some(next_start) =
3834            map_current_position_through_mutations(current_start, mutation_spans)
3835        else {
3836            return;
3837        };
3838        let Some(next_end) = map_current_position_through_mutations(current_end, mutation_spans)
3839        else {
3840            return;
3841        };
3842        if next_start >= next_end {
3843            return;
3844        }
3845        next_segments.push(TransformSpanMapSegmentV0 {
3846            current_start: next_start,
3847            current_end: next_end,
3848            original_start: segment.original_start + current_start - segment.current_start,
3849        });
3850    }
3851}
3852
3853fn map_current_position_through_mutations(
3854    position: usize,
3855    mutation_spans: &[TransformProvenanceMutationSpanV0],
3856) -> Option<usize> {
3857    let mut delta = 0isize;
3858    for span in mutation_spans {
3859        if position < span.source_span_start {
3860            return apply_position_delta(position, delta);
3861        }
3862        if position <= span.source_span_end {
3863            return (position == span.source_span_start)
3864                .then(|| apply_position_delta(span.generated_span_start, 0))
3865                .flatten()
3866                .or_else(|| {
3867                    (position == span.source_span_end)
3868                        .then(|| apply_position_delta(span.generated_span_end, 0))
3869                        .flatten()
3870                });
3871        }
3872        delta = span.generated_span_end as isize - span.source_span_end as isize;
3873    }
3874    apply_position_delta(position, delta)
3875}
3876
3877fn apply_position_delta(position: usize, delta: isize) -> Option<usize> {
3878    if delta >= 0 {
3879        position.checked_add(delta as usize)
3880    } else {
3881        position.checked_sub((-delta) as usize)
3882    }
3883}
3884
3885/// Stamp each mutation span with the stable node key of the innermost original-source
3886/// node it maps back to. `node_key` is **best-effort, additive metadata** (it never affects
3887/// emitted CSS): a span in a later pass whose current coordinates straddle a prior-pass
3888/// mutation boundary matches no single surviving segment, so it maps to `None` and the key
3889/// is omitted rather than mis-attributed. The common case (a span fully inside one surviving
3890/// region) maps through the composed coordinate map to the correct original interval.
3891fn stamp_mutation_span_node_keys(
3892    mutation_spans: &mut [TransformProvenanceMutationSpanV0],
3893    coordinate_map: &TransformSpanCoordinateMapV0,
3894    stable_ir_nodes: &[StableTransformIrNodeV0],
3895) {
3896    for span in mutation_spans {
3897        let Some((original_start, original_end)) = coordinate_map
3898            .map_current_span_to_original(span.source_span_start, span.source_span_end)
3899        else {
3900            continue;
3901        };
3902        span.node_key =
3903            innermost_stable_node_key_for_span(original_start, original_end, stable_ir_nodes);
3904    }
3905}
3906
3907fn innermost_stable_node_key_for_span(
3908    original_start: usize,
3909    original_end: usize,
3910    stable_ir_nodes: &[StableTransformIrNodeV0],
3911) -> Option<omena_transform_cst::StableNodeKeyV0> {
3912    stable_ir_nodes
3913        .iter()
3914        .filter(|node| {
3915            let overlap_start = node.source_span_start.max(original_start);
3916            let overlap_end = node.source_span_end.min(original_end);
3917            overlap_start < overlap_end
3918        })
3919        .min_by_key(|node| {
3920            let contains =
3921                node.source_span_start <= original_start && original_end <= node.source_span_end;
3922            (
3923                usize::from(!contains),
3924                node.source_span_end.saturating_sub(node.source_span_start),
3925            )
3926        })
3927        .and_then(|node| node.additive_node_key().cloned())
3928}
3929
3930#[cfg(test)]
3931mod dispatch_table_tests {
3932    use super::*;
3933    use crate::model::TransformCascadeEnvironmentV0;
3934    use omena_cascade_proof::DischargeLedgerVerdictV0;
3935    use omena_evidence_graph::EvidenceNodeKeyV0;
3936    use omena_parser::{
3937        ClosedWorldLinkedModuleV0, ConfigurationHashV0, ModuleIdV0, ModuleInstanceKeyV0,
3938    };
3939    use omena_transform_cst::{
3940        IrEditRegionV0, IrTransactionV0, TransformPassClassV0, default_transform_pass_descriptors,
3941    };
3942
3943    fn mutation_span(
3944        source_span_start: usize,
3945        source_span_end: usize,
3946        generated_span_start: usize,
3947        generated_span_end: usize,
3948    ) -> TransformProvenanceMutationSpanV0 {
3949        TransformProvenanceMutationSpanV0 {
3950            source_span_start,
3951            source_span_end,
3952            generated_span_start,
3953            generated_span_end,
3954            node_key: None,
3955        }
3956    }
3957
3958    #[test]
3959    fn strict_winner_difference_restores_the_input_ir() {
3960        let input_source = ".a { color: red; }";
3961        let output_source = ".a { color: blue; }";
3962        let input_ir =
3963            lower_transform_ir_from_source(input_source, StyleDialect::Css, "strict-policy-input");
3964        let output_ir = lower_transform_ir_from_source(
3965            output_source,
3966            StyleDialect::Css,
3967            "strict-policy-output",
3968        );
3969        let environment = TransformCascadeEnvironmentV0::default();
3970        let evaluation = evaluate_transform_winner_equality(
3971            TransformPassKind::RuleMerging,
3972            &input_ir,
3973            &output_ir,
3974            &[mutation_span(0, input_source.len(), 0, output_source.len())],
3975            StyleDialect::Css,
3976            TransformWinnerEqualityContextV0 {
3977                input_scope: SemanticObservationScopeV0::default(),
3978                output_scope: SemanticObservationScopeV0::default(),
3979                cascade_environment: Some(&environment),
3980            },
3981        );
3982        let reasons = strict_rollback_reasons(TransformPassKind::RuleMerging, &evaluation);
3983        assert!(
3984            reasons.iter().any(|reason| matches!(
3985                reason,
3986                TransformStrictPolicyReasonV0::WinnerChanged { .. }
3987            ))
3988        );
3989
3990        let mut document = TransformExecutionDocumentV0::new(output_source, StyleDialect::Css);
3991        let dispatch_result = TransformPassDispatchResultV0::textual_mutation(
3992            TransformPassKind::RuleMerging.id(),
3993            input_source.len(),
3994            output_source.to_string(),
3995            1,
3996            "test mutation",
3997        );
3998        let mut summary = TransformStrictPolicySummaryV0::for_profile("strict-verification");
3999        let dispatch_result = apply_strict_winner_rollback(
4000            TransformPassKind::RuleMerging,
4001            &input_ir,
4002            &mut document,
4003            reasons,
4004            dispatch_result,
4005            &mut summary,
4006        );
4007
4008        assert_eq!(document.current_css(), input_source);
4009        assert_eq!(summary.rolled_back_count, 1);
4010        assert!(matches!(
4011            dispatch_result.decision,
4012            TransformDecisionDraftV0::Rejected {
4013                reason: TransformRejectionReasonV0::StrictVerification { .. },
4014                ..
4015            }
4016        ));
4017    }
4018
4019    #[test]
4020    fn reachability_precision_preserves_witness_bound_exactness() -> Result<(), String> {
4021        let context = TransformExecutionContextV0::default();
4022
4023        assert_eq!(
4024            classify_transform_reachability_precision(&context, true, Some(FactPrecision::Exact)),
4025            FactPrecision::Exact
4026        );
4027        assert_eq!(
4028            classify_transform_reachability_precision(
4029                &context,
4030                true,
4031                Some(FactPrecision::Heuristic),
4032            ),
4033            FactPrecision::Heuristic
4034        );
4035        assert_eq!(
4036            classify_transform_reachability_precision(&context, false, None),
4037            FactPrecision::Unknown
4038        );
4039        Ok(())
4040    }
4041
4042    #[test]
4043    fn text_local_dispatch_handlers_match_pass_descriptors() {
4044        let mut descriptor_pass_ids = default_transform_pass_descriptors()
4045            .into_iter()
4046            .filter(|descriptor| descriptor.pass_class == TransformPassClassV0::TextLocal)
4047            .map(|descriptor| descriptor.id)
4048            .collect::<Vec<_>>();
4049        let mut handler_pass_ids = text_local_pass_handlers()
4050            .iter()
4051            .map(|handler| handler.kind.id())
4052            .collect::<Vec<_>>();
4053
4054        descriptor_pass_ids.sort_unstable();
4055        handler_pass_ids.sort_unstable();
4056
4057        assert_eq!(handler_pass_ids.len(), 20);
4058        assert_eq!(handler_pass_ids, descriptor_pass_ids);
4059    }
4060
4061    #[test]
4062    fn discharge_decisions_block_stale_and_record_ledger_evidence() {
4063        let missing_lookup = omena_cascade_proof::DischargeLedgerLookupV0 {
4064            schema_version: "0",
4065            product: "omena-cascade-proof.discharge-ledger.lookup",
4066            cell_key: "missing-cell".to_string(),
4067            status: omena_cascade_proof::DischargeLedgerLookupStatusV0::Missing,
4068            obligation_family: None,
4069            cell_family: None,
4070            verdict: None,
4071            boundedness_kind: None,
4072            floor_reason: Some("ledger cell is absent"),
4073        };
4074        let accepted_without_stamp = TransformCascadeProofObligationV0 {
4075            pass_id: "scope-flatten",
4076            proof_product: "omena-cascade.scope-flatten-proof",
4077            accepted: true,
4078            blocked_reason: None,
4079            provenance_preserved: true,
4080            cascade_safe_witness: "scope flatten proof accepted".to_string(),
4081            source_span_start: Some(0),
4082            source_span_end: Some(1),
4083            checked_obligations: vec!["rootScopeOnly"],
4084            canonical_smt_input: None,
4085            discharge_ledger_lookup: Some(missing_lookup),
4086            discharge_evidence: None,
4087            proof_payload: serde_json::json!({ "accepted": true }),
4088        };
4089        assert_eq!(
4090            flatten_discharge_precondition_failure(
4091                Some(TransformPassKind::ScopeFlatten),
4092                std::slice::from_ref(&accepted_without_stamp),
4093            ),
4094            Some(TransformBlockedReasonV0::DischargeMissing {
4095                lookup_status: Some(DischargeLedgerLookupStatusV0::Missing),
4096                verdict: None,
4097            })
4098        );
4099
4100        let mut accepted_with_stale_lookup = accepted_without_stamp.clone();
4101        accepted_with_stale_lookup.discharge_ledger_lookup =
4102            Some(omena_cascade_proof::DischargeLedgerLookupV0 {
4103                schema_version: "0",
4104                product: "omena-cascade-proof.discharge-ledger.lookup",
4105                cell_key: "stale-cell".to_string(),
4106                status: DischargeLedgerLookupStatusV0::Stale,
4107                obligation_family: None,
4108                cell_family: None,
4109                verdict: None,
4110                boundedness_kind: None,
4111                floor_reason: Some("ledger pins do not match the runtime"),
4112            });
4113        let stale_reason = flatten_discharge_precondition_failure(
4114            Some(TransformPassKind::ScopeFlatten),
4115            std::slice::from_ref(&accepted_with_stale_lookup),
4116        );
4117        assert_eq!(
4118            stale_reason,
4119            Some(TransformBlockedReasonV0::DischargeMissing {
4120                lookup_status: Some(DischargeLedgerLookupStatusV0::Stale),
4121                verdict: None,
4122            })
4123        );
4124        let stale_wire = serde_json::to_value(stale_reason);
4125        assert!(stale_wire.is_ok());
4126        let Ok(stale_wire) = stale_wire else {
4127            return;
4128        };
4129        assert_eq!(stale_wire["lookupStatus"], "stale");
4130        assert!(stale_wire.get("lookup_status").is_none());
4131
4132        let fresh_lookup = omena_cascade_proof::DischargeLedgerLookupV0 {
4133            schema_version: "0",
4134            product: "omena-cascade-proof.discharge-ledger.lookup",
4135            cell_key: "fresh-cell".to_string(),
4136            status: omena_cascade_proof::DischargeLedgerLookupStatusV0::Matched,
4137            obligation_family: Some("ScopedMatching".to_string()),
4138            cell_family: Some("scope-flatten-candidate".to_string()),
4139            verdict: Some(omena_cascade_proof::DischargeLedgerVerdictV0::Accepted),
4140            boundedness_kind: Some("exact".to_string()),
4141            floor_reason: None,
4142        };
4143        let accepted_with_stamp = TransformCascadeProofObligationV0 {
4144            pass_id: "scope-flatten",
4145            proof_product: "omena-cascade.scope-flatten-proof",
4146            accepted: true,
4147            blocked_reason: None,
4148            provenance_preserved: true,
4149            cascade_safe_witness: "scope flatten proof accepted".to_string(),
4150            source_span_start: Some(0),
4151            source_span_end: Some(1),
4152            checked_obligations: vec!["rootScopeOnly"],
4153            canonical_smt_input: None,
4154            discharge_ledger_lookup: Some(fresh_lookup.clone()),
4155            discharge_evidence: Some(TransformDischargeEvidenceV0 {
4156                evidence_node_key: EvidenceNodeKeyV0::new(
4157                    "omena-cascade-proof.cascade-proof-record",
4158                    "fresh-obligation",
4159                ),
4160                guarantee_family: GuaranteeFamilyV0::LedgerBackedObligationDischarge,
4161                ledger_cell_key: "fresh-cell".to_string(),
4162                boundedness_kind: "exact".to_string(),
4163            }),
4164            proof_payload: serde_json::json!({ "accepted": true }),
4165        };
4166        let accepted_without_evidence = TransformCascadeProofObligationV0 {
4167            discharge_evidence: None,
4168            ..accepted_with_stamp.clone()
4169        };
4170        assert_eq!(
4171            flatten_discharge_precondition_failure(
4172                Some(TransformPassKind::ScopeFlatten),
4173                &[accepted_with_stamp.clone(), accepted_without_evidence],
4174            ),
4175            Some(TransformBlockedReasonV0::DischargeMissing {
4176                lookup_status: Some(DischargeLedgerLookupStatusV0::Matched),
4177                verdict: Some(DischargeLedgerVerdictV0::Accepted),
4178            })
4179        );
4180
4181        let mut mismatched_evidence = accepted_with_stamp.clone();
4182        if let Some(evidence) = mismatched_evidence.discharge_evidence.as_mut() {
4183            evidence.ledger_cell_key = "different-cell".to_string();
4184        }
4185        assert_eq!(
4186            flatten_discharge_precondition_failure(
4187                Some(TransformPassKind::ScopeFlatten),
4188                std::slice::from_ref(&mismatched_evidence),
4189            ),
4190            Some(TransformBlockedReasonV0::DischargeMissing {
4191                lookup_status: Some(DischargeLedgerLookupStatusV0::Matched),
4192                verdict: Some(DischargeLedgerVerdictV0::Accepted),
4193            })
4194        );
4195
4196        assert_eq!(
4197            flatten_discharge_precondition_failure(
4198                Some(TransformPassKind::ScopeFlatten),
4199                std::slice::from_ref(&accepted_with_stamp),
4200            ),
4201            None
4202        );
4203        assert_eq!(
4204            flatten_discharge_evidence(
4205                Some(TransformPassKind::ScopeFlatten),
4206                std::slice::from_ref(&accepted_with_stamp),
4207            )
4208            .len(),
4209            1
4210        );
4211        assert_eq!(
4212            flatten_discharge_precondition_failure(Some(TransformPassKind::NestingUnwrap), &[]),
4213            None
4214        );
4215        assert_eq!(
4216            flatten_discharge_precondition_failure(Some(TransformPassKind::LayerFlatten), &[]),
4217            Some(TransformBlockedReasonV0::DischargeMissing {
4218                lookup_status: None,
4219                verdict: None,
4220            })
4221        );
4222
4223        let execution = execute_transform_passes_on_source(
4224            "@scope (:root) { .card { color: red; } }",
4225            &[TransformPassKind::ScopeFlatten],
4226        );
4227        let applied = execution
4228            .decisions
4229            .iter()
4230            .find(|decision| decision.compatibility_outcome().pass_id == "scope-flatten");
4231        assert!(matches!(
4232            applied,
4233            Some(TransformDecision::Applied {
4234                discharge_evidence,
4235                ..
4236            }) if discharge_evidence.len() == 1
4237                && discharge_evidence[0].guarantee_family
4238                    == GuaranteeFamilyV0::LedgerBackedObligationDischarge
4239                && discharge_evidence[0].ledger_cell_key.len() == 64
4240        ));
4241        let applied_wire = serde_json::to_value(applied);
4242        assert!(applied_wire.is_ok());
4243        let Ok(applied_wire) = applied_wire else {
4244            return;
4245        };
4246        assert_eq!(
4247            applied_wire["dischargeEvidence"][0]["boundednessKind"],
4248            "exact"
4249        );
4250        assert!(applied_wire.get("discharge_evidence").is_none());
4251    }
4252
4253    #[test]
4254    fn runtime_dispatch_entries_cover_public_registry_entries() {
4255        let registry = default_transform_pass_registry();
4256
4257        assert!(
4258            registry
4259                .entries
4260                .iter()
4261                .all(|entry| { runtime_pass_implementation_for_entry(entry).is_some() })
4262        );
4263        assert!(registry.entries.iter().all(|entry| {
4264            matches!(
4265                (
4266                    entry.dispatch_kind,
4267                    runtime_pass_implementation_for_entry(entry)
4268                ),
4269                (
4270                    TransformPassDispatchKindV0::TextLocalSliceRewrite,
4271                    Some(TransformRuntimePassImplementationV0::TextLocal(_))
4272                ) | (
4273                    TransformPassDispatchKindV0::StructuralIrTransaction,
4274                    Some(TransformRuntimePassImplementationV0::Structural(_))
4275                ) | (
4276                    TransformPassDispatchKindV0::ModuleEvaluationHandler,
4277                    Some(TransformRuntimePassImplementationV0::ModuleEvaluation(_))
4278                ) | (
4279                    TransformPassDispatchKindV0::EmissionBoundary,
4280                    Some(TransformRuntimePassImplementationV0::Emission(_))
4281                )
4282            )
4283        }));
4284    }
4285
4286    #[test]
4287    fn text_local_dispatch_handlers_declare_ir_window_scopes() {
4288        let handlers = text_local_pass_handlers();
4289
4290        assert_eq!(handlers.len(), 20);
4291        assert_eq!(
4292            handlers
4293                .iter()
4294                .filter(|handler| handler.window_scope
4295                    == TransformTextLocalWindowScopeV0::DocumentTokenStream)
4296                .count(),
4297            2
4298        );
4299        assert_eq!(
4300            handlers
4301                .iter()
4302                .filter(|handler| handler.window_scope == TransformTextLocalWindowScopeV0::Selector)
4303                .count(),
4304            1
4305        );
4306        assert_eq!(
4307            handlers
4308                .iter()
4309                .filter(|handler| handler.window_scope
4310                    == TransformTextLocalWindowScopeV0::DeclarationBlock)
4311                .count(),
4312            4
4313        );
4314        assert_eq!(
4315            handlers
4316                .iter()
4317                .filter(|handler| handler.window_scope
4318                    == TransformTextLocalWindowScopeV0::DeclarationValue)
4319                .count(),
4320            13
4321        );
4322
4323        assert!(handlers.iter().any(|handler| {
4324            handler.kind == TransformPassKind::WhitespaceStrip
4325                && handler.window_scope == TransformTextLocalWindowScopeV0::DocumentTokenStream
4326        }));
4327        assert!(handlers.iter().any(|handler| {
4328            handler.kind == TransformPassKind::SelectorIsWhereCompression
4329                && handler.window_scope == TransformTextLocalWindowScopeV0::Selector
4330        }));
4331        assert!(handlers.iter().any(|handler| {
4332            handler.kind == TransformPassKind::ShorthandCombining
4333                && handler.window_scope == TransformTextLocalWindowScopeV0::DeclarationBlock
4334        }));
4335        assert!(handlers.iter().any(|handler| {
4336            handler.kind == TransformPassKind::CalcReduction
4337                && handler.window_scope == TransformTextLocalWindowScopeV0::DeclarationValue
4338        }));
4339    }
4340
4341    #[test]
4342    fn text_local_dispatch_handlers_declare_execution_modes() {
4343        let handlers = text_local_pass_handlers();
4344
4345        assert_eq!(handlers.len(), 20);
4346        assert_eq!(
4347            handlers
4348                .iter()
4349                .filter(|handler| handler.execution_mode
4350                    == TransformTextLocalExecutionModeV0::FullDocument)
4351                .count(),
4352            6
4353        );
4354        assert_eq!(
4355            handlers
4356                .iter()
4357                .filter(|handler| handler.execution_mode
4358                    == TransformTextLocalExecutionModeV0::WindowBatch)
4359                .count(),
4360            14
4361        );
4362
4363        assert!(handlers.iter().any(|handler| {
4364            handler.kind == TransformPassKind::ValueResolution
4365                && handler.execution_mode == TransformTextLocalExecutionModeV0::FullDocument
4366        }));
4367        assert!(handlers.iter().any(|handler| {
4368            handler.kind == TransformPassKind::CalcReduction
4369                && handler.execution_mode == TransformTextLocalExecutionModeV0::WindowBatch
4370        }));
4371    }
4372
4373    #[test]
4374    fn structural_dispatch_handlers_match_remaining_structural_descriptors() {
4375        let mut descriptor_pass_ids = default_transform_pass_descriptors()
4376            .into_iter()
4377            .filter(|descriptor| descriptor.pass_class == TransformPassClassV0::Structural)
4378            .map(|descriptor| descriptor.id)
4379            .collect::<Vec<_>>();
4380        let mut handler_pass_ids = structural_pass_handlers()
4381            .iter()
4382            .map(|handler| handler.kind.id())
4383            .collect::<Vec<_>>();
4384
4385        descriptor_pass_ids.sort_unstable();
4386        handler_pass_ids.sort_unstable();
4387
4388        assert_eq!(descriptor_pass_ids.len(), 21);
4389        assert_eq!(handler_pass_ids.len(), 21);
4390        assert_eq!(handler_pass_ids, descriptor_pass_ids);
4391    }
4392
4393    #[test]
4394    fn structural_dispatch_input_carries_ir_not_raw_css() -> Result<(), String> {
4395        let source = std::fs::read_to_string(
4396            std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
4397                .join("src")
4398                .join("runtime")
4399                .join("executor.rs"),
4400        )
4401        .map_err(|err| format!("executor source should be readable: {err:?}"))?;
4402        let input_anchor = source
4403            .find("struct TransformStructuralPassInputV0")
4404            .ok_or_else(|| "structural input should exist".to_string())?;
4405        let handler_anchor = source[input_anchor..]
4406            .find("fn structural_pass_handlers")
4407            .ok_or_else(|| "structural handler boundary should exist".to_string())?;
4408        let input_body = &source[input_anchor..input_anchor + handler_anchor];
4409
4410        assert!(input_body.contains("current_ir: &'a mut TransformIrV0"));
4411        assert!(!input_body.contains("input_css:"));
4412        assert!(!input_body.contains("fn source_text(&self) -> &str"));
4413        assert!(input_body.contains("fn current_ir_mut(&mut self) -> &mut TransformIrV0"));
4414        Ok(())
4415    }
4416
4417    #[test]
4418    fn text_local_dispatch_uses_ir_window_input() -> Result<(), String> {
4419        let source = std::fs::read_to_string(
4420            std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
4421                .join("src")
4422                .join("runtime")
4423                .join("executor.rs"),
4424        )
4425        .map_err(|err| format!("executor source should be readable: {err:?}"))?;
4426        let runner_anchor = source
4427            .find("type TransformTextLocalRunnerV0")
4428            .ok_or_else(|| "text-local runner type should exist".to_string())?;
4429        let structural_runner_anchor = source[runner_anchor..]
4430            .find("type TransformStructuralRunnerV0")
4431            .ok_or_else(|| "structural runner type should delimit text-local input".to_string())?;
4432        let text_local_input_body =
4433            &source[runner_anchor..runner_anchor + structural_runner_anchor];
4434
4435        assert!(text_local_input_body.contains("TransformTextLocalPassInputV0<'a>"));
4436        assert!(text_local_input_body.contains("TransformTextLocalPassOutputV0<'a>"));
4437        assert!(text_local_input_body.contains("TransformTextLocalIrWindowV0"));
4438        assert!(!text_local_input_body.contains("fn(&str, StyleDialect"));
4439        assert!(text_local_input_body.contains("fn rewrite_windows("));
4440        assert!(text_local_input_body.contains("fn rewrite_full_document("));
4441
4442        let dispatch_anchor = source
4443            .find("fn dispatch_text_local_pass")
4444            .ok_or_else(|| "text-local dispatch should exist".to_string())?;
4445        let whitespace_anchor = source[dispatch_anchor..]
4446            .find("fn run_whitespace_strip_text_local")
4447            .ok_or_else(|| "first text-local runner should delimit dispatch".to_string())?;
4448        let dispatch_body = &source[dispatch_anchor..dispatch_anchor + whitespace_anchor];
4449
4450        assert!(dispatch_body.contains("current_ir: &TransformIrV0"));
4451        assert!(dispatch_body.contains("TransformTextLocalPassInputV0::from_ir("));
4452        assert!(dispatch_body.contains("handler.window_scope"));
4453        assert!(dispatch_body.contains("handler.execution_mode"));
4454        assert!(!dispatch_body.contains("input_css: &str"));
4455
4456        let loop_anchor = source
4457            .find("Some(TransformRuntimePassImplementationV0::TextLocal(handler))")
4458            .ok_or_else(|| "text-local executor dispatch branch should exist".to_string())?;
4459        let module_anchor = source[loop_anchor..]
4460            .find("Some(TransformRuntimePassImplementationV0::ModuleEvaluation(pass))")
4461            .ok_or_else(|| "module branch should delimit text-local branch".to_string())?;
4462        let loop_body = &source[loop_anchor..loop_anchor + module_anchor];
4463
4464        assert!(loop_body.contains("dispatch_text_local_pass("));
4465        assert!(loop_body.contains("&document.current_ir"));
4466        assert!(!loop_body.contains("pass_input_css, dialect, context"));
4467        Ok(())
4468    }
4469
4470    #[test]
4471    fn text_local_declaration_scopes_rewrite_multiple_ir_windows() {
4472        let source = ".a { color: RED; } .b { color: BLUE; }";
4473        let ir = lower_transform_ir_from_source(source, StyleDialect::Css, "window-batch-test");
4474        let context = TransformExecutionContextV0::default();
4475        let input = TransformTextLocalPassInputV0::from_ir(
4476            &ir,
4477            TransformTextLocalWindowScopeV0::DeclarationValue,
4478            TransformTextLocalExecutionModeV0::WindowBatch,
4479            StyleDialect::Css,
4480            &context,
4481        );
4482
4483        assert_eq!(input.source_windows.len(), 2);
4484
4485        let output = input.rewrite_text_local(|window_source, _dialect, _context| {
4486            (window_source.to_ascii_lowercase(), 1)
4487        });
4488
4489        assert_eq!(output.input_byte_len(), source.len());
4490        assert_eq!(output.mutation_count, 2);
4491        assert_eq!(output.provenance_mutation_spans().len(), 2);
4492        assert_eq!(
4493            &source[output.provenance_mutation_spans()[0].source_span_start
4494                ..output.provenance_mutation_spans()[0].source_span_end],
4495            "RED"
4496        );
4497        assert_eq!(
4498            &output.rewritten_css[output.provenance_mutation_spans()[0].generated_span_start
4499                ..output.provenance_mutation_spans()[0].generated_span_end],
4500            "red"
4501        );
4502        assert_eq!(
4503            &source[output.provenance_mutation_spans()[1].source_span_start
4504                ..output.provenance_mutation_spans()[1].source_span_end],
4505            "BLUE"
4506        );
4507        assert_eq!(
4508            &output.rewritten_css[output.provenance_mutation_spans()[1].generated_span_start
4509                ..output.provenance_mutation_spans()[1].generated_span_end],
4510            "blue"
4511        );
4512        assert_eq!(
4513            output.into_document_css(),
4514            ".a { color: red; } .b { color: blue; }"
4515        );
4516    }
4517
4518    #[test]
4519    fn structural_dispatch_handlers_commit_through_ir_mutation_only() -> Result<(), String> {
4520        let source = std::fs::read_to_string(
4521            std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
4522                .join("src")
4523                .join("runtime")
4524                .join("executor.rs"),
4525        )
4526        .map_err(|err| format!("executor source should be readable: {err:?}"))?;
4527        let first_structural_handler = source
4528            .find("fn run_import_inline_structural")
4529            .ok_or_else(|| "first structural handler should exist".to_string())?;
4530        let executor_loop_anchor = source[first_structural_handler..]
4531            .find("fn execute_transform_passes_on_source_with_active_lex_cache")
4532            .ok_or_else(|| "executor loop should delimit structural handlers".to_string())?;
4533        let structural_handler_body =
4534            &source[first_structural_handler..first_structural_handler + executor_loop_anchor];
4535        let ir_mutation_count = structural_handler_body
4536            .matches("input.ir_mutation_result(")
4537            .count();
4538
4539        assert_eq!(ir_mutation_count, structural_pass_handlers().len());
4540        assert!(
4541            !structural_handler_body.contains("TransformPassDispatchResultV0::textual_mutation(")
4542        );
4543        assert!(!structural_handler_body.contains("TransformPassDispatchResultV0::ir_mutation("));
4544        assert!(!structural_handler_body.contains("_rendered_css"));
4545        assert!(!structural_handler_body.contains("input.source_text("));
4546        Ok(())
4547    }
4548
4549    #[test]
4550    fn structural_ir_mutations_do_not_relower_document_from_css() -> Result<(), String> {
4551        let source = std::fs::read_to_string(
4552            std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
4553                .join("src")
4554                .join("runtime")
4555                .join("executor.rs"),
4556        )
4557        .map_err(|err| format!("executor source should be readable: {err:?}"))?;
4558        let ir_mutation_anchor = source
4559            .find("fn ir_mutation(")
4560            .ok_or_else(|| "IR mutation dispatch result constructor should exist".to_string())?;
4561        let planned_only_anchor = source[ir_mutation_anchor..]
4562            .find("fn from_mutation_outcome(")
4563            .ok_or_else(|| {
4564                "mutation decision constructor should delimit IR mutation".to_string()
4565            })?;
4566        let ir_mutation_body =
4567            &source[ir_mutation_anchor..ir_mutation_anchor + planned_only_anchor];
4568
4569        assert!(ir_mutation_body.contains("result.document_ir_updated = true;"));
4570
4571        let update_anchor = source
4572            .find("if document_ir_updated")
4573            .ok_or_else(|| "executor should branch structural IR updates".to_string())?;
4574        let outcomes_anchor = source[update_anchor..]
4575            .find("outcomes.push(outcome);")
4576            .ok_or_else(|| "outcome push should delimit document update".to_string())?;
4577        let update_body = &source[update_anchor..update_anchor + outcomes_anchor];
4578        let text_branch_anchor = update_body
4579            .find("match next_textual_css")
4580            .ok_or_else(|| "text-local/module output branch should exist".to_string())?;
4581        let relower_anchor = update_body
4582            .find("document.replace_with_css(next_css);")
4583            .ok_or_else(|| {
4584                "text-local/module document re-lowering path should exist".to_string()
4585            })?;
4586        let ir_branch_body = &update_body[..text_branch_anchor];
4587
4588        assert!(relower_anchor > text_branch_anchor);
4589        assert!(!ir_branch_body.contains("document.replace_with_css(next_css);"));
4590        Ok(())
4591    }
4592
4593    #[test]
4594    fn executor_loop_materializes_textual_bridge_lazily() -> Result<(), String> {
4595        let source = std::fs::read_to_string(
4596            std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
4597                .join("src")
4598                .join("runtime")
4599                .join("executor.rs"),
4600        )
4601        .map_err(|err| format!("executor source should be readable: {err:?}"))?;
4602        let loop_anchor = source
4603            .find("for (pass_index, pass_id) in ordered_pass_ids.iter().enumerate()")
4604            .ok_or_else(|| "executor pass loop should exist".to_string())?;
4605        let outcomes_anchor = source[loop_anchor..]
4606            .find("outcomes.push(outcome);")
4607            .ok_or_else(|| "outcome push should delimit pass loop body".to_string())?;
4608        let loop_body = &source[loop_anchor..loop_anchor + outcomes_anchor];
4609
4610        assert!(loop_body.contains("TransformTextualBridgeSnapshotV0::default()"));
4611        assert!(loop_body.contains("textual_bridge.materialize_current_css(&document);"));
4612        assert!(loop_body.contains("if should_maintain_document_lex_cache"));
4613        assert!(loop_body.contains("collect_cascade_proof_obligations_for_ir_pass_input("));
4614        assert!(loop_body.contains("dispatch_structural_pass("));
4615        assert!(loop_body.contains("Some(mutation_spans) => mutation_spans"));
4616        assert!(loop_body.contains("if document_ir_updated"));
4617        assert!(loop_body.contains("let output_byte_len = document.current_byte_len();"));
4618        assert!(loop_body.contains("let next_css = document.current_css().to_string();"));
4619        assert!(loop_body.contains("match next_textual_css"));
4620        assert!(loop_body.contains("derive_transform_mutation_spans(pass_input_css, &next_css)"));
4621        assert!(loop_body.contains("outcome_mutation_spans.push(Vec::new());"));
4622        Ok(())
4623    }
4624
4625    #[test]
4626    fn structural_single_transaction_uses_ir_mutation_span_envelope() -> Result<(), String> {
4627        let source = ".button { color: red; }";
4628        let selector_end = source
4629            .find('{')
4630            .ok_or_else(|| "fixture should contain a block".to_string())?;
4631        let context = TransformExecutionContextV0 {
4632            class_name_rewrites: vec![crate::TransformClassNameRewriteV0 {
4633                original_name: "button".to_string(),
4634                rewritten_name: "_button_x".to_string(),
4635            }],
4636            ..TransformExecutionContextV0::default()
4637        };
4638        let execution = execute_transform_passes_on_source_with_dialect_and_context(
4639            source,
4640            StyleDialect::Css,
4641            &[TransformPassKind::HashCssModuleClassNames],
4642            &context,
4643        );
4644        let mutation_spans = &execution.provenance_derivation_forest.nodes[0].mutation_spans;
4645
4646        assert_eq!(execution.output_css, "._button_x{ color: red; }");
4647        assert_eq!(mutation_spans.len(), 1);
4648        assert_eq!(mutation_spans[0].source_span_start, 0);
4649        assert_eq!(mutation_spans[0].source_span_end, selector_end);
4650        assert!(mutation_spans[0].source_span_end < source.len());
4651        Ok(())
4652    }
4653
4654    #[test]
4655    fn structural_multi_transaction_batches_compose_mutation_span_coordinates() {
4656        let spans = compose_ir_transaction_mutation_span_batches(
4657            6,
4658            10,
4659            &[
4660                vec![mutation_span(1, 3, 1, 5)],
4661                vec![mutation_span(6, 7, 6, 9)],
4662            ],
4663        );
4664
4665        assert_eq!(
4666            spans,
4667            Some(vec![mutation_span(1, 3, 1, 5), mutation_span(4, 5, 6, 9)])
4668        );
4669    }
4670
4671    #[test]
4672    fn structural_multi_transaction_batches_fall_back_when_coordinates_do_not_project() {
4673        let spans = compose_ir_transaction_mutation_span_batches(
4674            6,
4675            8,
4676            &[
4677                vec![mutation_span(1, 3, 1, 5)],
4678                vec![mutation_span(4, 6, 4, 6)],
4679            ],
4680        );
4681
4682        assert_eq!(spans, None);
4683    }
4684
4685    #[test]
4686    fn structural_multi_transaction_pass_uses_composed_ir_mutation_spans() {
4687        let source = ".a { color: red; color: blue; }.dup { margin: 0; }.dup { margin: 0; }";
4688        let execution = execute_transform_passes_on_source_with_dialect_and_context(
4689            source,
4690            StyleDialect::Css,
4691            &[TransformPassKind::RuleDeduplication],
4692            &TransformExecutionContextV0::default(),
4693        );
4694        let mutation_spans = &execution.provenance_derivation_forest.nodes[0].mutation_spans;
4695
4696        assert_eq!(
4697            execution
4698                .structural_ir_transaction_telemetry
4699                .transaction_commit_count,
4700            2
4701        );
4702        let ir_telemetry = execution.structural_ir_transaction_telemetry;
4703        assert_eq!(ir_telemetry.ir_transaction_commit_count, 2);
4704        assert_eq!(ir_telemetry.ir_materialization_count, 2);
4705        assert_eq!(ir_telemetry.ir_metadata_refresh_count, 4);
4706        assert_eq!(ir_telemetry.ir_mutation_count, 2);
4707        assert_eq!(
4708            ir_telemetry.ir_metadata_refresh_count,
4709            ir_telemetry.ir_transaction_commit_count + ir_telemetry.ir_materialization_count,
4710            "each metadata refresh must be attributed to one successful commit or materialization"
4711        );
4712        assert_eq!(
4713            ir_telemetry.ir_transaction_commit_count,
4714            execution
4715                .structural_ir_transaction_telemetry
4716                .transaction_commit_count,
4717            "this helper-only fixture pins the corollary without conflating the two commit domains"
4718        );
4719        assert_eq!(execution.mutation_count, 2);
4720        assert_eq!(mutation_spans.len(), 2);
4721        assert!(mutation_spans.iter().all(|span| {
4722            span.source_span_start <= span.source_span_end
4723                && span.generated_span_start <= span.generated_span_end
4724                && span.generated_span_end <= execution.output_css.len()
4725        }));
4726    }
4727
4728    #[test]
4729    fn lex_cache_consumer_classification_stays_text_local() {
4730        for descriptor in default_transform_pass_descriptors() {
4731            assert_eq!(
4732                transform_pass_may_consume_lex_cache(descriptor.kind),
4733                descriptor.pass_class == TransformPassClassV0::TextLocal,
4734                "unexpected lex-cache consumer classification for {}",
4735                descriptor.id
4736            );
4737        }
4738    }
4739
4740    #[test]
4741    fn structural_dispatch_results_never_return_textual_css() -> Result<(), String> {
4742        let source = r#"
4743@import "./tokens.css";
4744@layer theme { .card { color: red; } }
4745@scope (:root) { .scoped { color: red; } }
4746.dup { color: red; } .dup { color: red; }
4747.empty {}
4748@supports not (display: grid) { .supports-dead { color: red; } }
4749@media not all { .media-dead { color: red; } }
4750@container (max-width: -1px) { .container-dead { color: red; } }
4751@when supports(display: grid) { .grid { display: grid; } } @else { .fallback { display: block; } }
4752.button { composes: base utility; color: var(--pkg-brand); }
4753.base { color: blue; } .utility { color: green; }
4754.used { animation: spin 1s; --keep: green; color: var(--keep); }
4755.unused { color: red; --dead: red; }
4756@keyframes spin { to { opacity: 1; } }
4757@keyframes fade { to { opacity: 0; } }
4758@value keep: 1px; @value dead: 2px; :export { keepExport: keep; deadExport: dead; }
4759"#;
4760        let context = TransformExecutionContextV0 {
4761            import_inlines: vec![TransformImportInlineV0 {
4762                import_source: "./tokens.css".to_string(),
4763                replacement_css: ":root { --pkg-brand: #123456; }".to_string(),
4764            }],
4765            class_name_rewrites: vec![
4766                crate::TransformClassNameRewriteV0 {
4767                    original_name: "button".to_string(),
4768                    rewritten_name: "_button_hash".to_string(),
4769                },
4770                crate::TransformClassNameRewriteV0 {
4771                    original_name: "base".to_string(),
4772                    rewritten_name: "_base_hash".to_string(),
4773                },
4774                crate::TransformClassNameRewriteV0 {
4775                    original_name: "utility".to_string(),
4776                    rewritten_name: "_utility_hash".to_string(),
4777                },
4778            ],
4779            css_module_composes_resolutions: vec![TransformCssModuleComposesResolutionV0 {
4780                local_class_name: "button".to_string(),
4781                exported_class_names: vec!["base".to_string(), "utility".to_string()],
4782            }],
4783            design_token_routes: vec![TransformDesignTokenRouteV0 {
4784                token_name: "--pkg-brand".to_string(),
4785                routed_value: "#123456".to_string(),
4786            }],
4787            ..TransformExecutionContextV0::default()
4788        };
4789        let bundle = structural_dispatch_fixture_bundle()?;
4790
4791        for handler in structural_pass_handlers() {
4792            let mut ir = lower_transform_ir_from_source(
4793                source,
4794                StyleDialect::Css,
4795                "omena-transform-passes.structural-dispatch-fixture",
4796            );
4797            let input_byte_len = ir.source_text().len();
4798            let decision_policy = transform_structural_decision_policy(handler.kind)
4799                .ok_or_else(|| format!("missing decision policy for {}", handler.kind.id()))?;
4800            let result = (handler.run)(TransformStructuralPassInputV0 {
4801                pass_id: handler.kind.id(),
4802                kind: handler.kind,
4803                decision_policy,
4804                reachability_precision: FactPrecision::Conservative,
4805                current_ir: &mut ir,
4806                input_byte_len,
4807                dialect: StyleDialect::Css,
4808                context: &context,
4809                closed_world_bundle: Some(&bundle),
4810                module_qualified_symbols: None,
4811                module_reachable_class_names: None,
4812            });
4813
4814            assert!(
4815                result.next_textual_css.is_none(),
4816                "structural handler {} must not return textual CSS",
4817                handler.kind.id()
4818            );
4819            if result.decision.compatibility_outcome().mutation_count > 0 {
4820                assert!(
4821                    result.document_ir_updated,
4822                    "structural handler {} reported a mutation without an IR update",
4823                    handler.kind.id()
4824                );
4825            }
4826        }
4827        Ok(())
4828    }
4829
4830    #[test]
4831    fn semantic_preservation_refuses_mismatching_structural_rewrite() {
4832        let input_css = ".card { color: red; }";
4833        let mut document = TransformExecutionDocumentV0::new(input_css, StyleDialect::Css);
4834        let input_ir = document.current_ir.clone();
4835        document.replace_with_css(".card { color: blue; }".to_string());
4836        let output_byte_len = document.current_byte_len();
4837        let mut telemetry = TransformSemanticPreservationTelemetryV0::default();
4838        let dispatch_result = TransformPassDispatchResultV0::ir_mutation(
4839            TransformPassKind::RuleDeduplication.id(),
4840            input_css.len(),
4841            output_byte_len,
4842            1,
4843            "test structural rewrite",
4844        );
4845
4846        let enforcement_context = TransformSemanticPreservationEnforcementContextV0 {
4847            closed_world_bundle: None,
4848            module_qualified_symbols: None,
4849            module_reachable_class_names: None,
4850            projection: &SemanticObservationProjectionV0::default(),
4851            mutation_spans: &[TransformProvenanceMutationSpanV0 {
4852                source_span_start: 0,
4853                source_span_end: input_css.len(),
4854                generated_span_start: 0,
4855                generated_span_end: output_byte_len,
4856                node_key: None,
4857            }],
4858            cascade_environment: None,
4859            observe_pending_textual_output: false,
4860        };
4861        let (checked, winner_equality) = enforce_semantic_preservation_for_dispatch_result(
4862            TransformPassKind::RuleDeduplication,
4863            &input_ir,
4864            &mut document,
4865            dispatch_result,
4866            &mut telemetry,
4867            enforcement_context,
4868        );
4869
4870        assert!(winner_equality.is_none());
4871        assert_eq!(document.current_css(), input_css);
4872        assert!(!checked.document_ir_updated);
4873        assert!(matches!(
4874            checked.decision,
4875            TransformDecisionDraftV0::Rejected {
4876                reason: TransformRejectionReasonV0::SemanticPreservation,
4877                ..
4878            }
4879        ));
4880        assert_eq!(
4881            checked.decision.compatibility_outcome().status,
4882            TransformPassRuntimeStatus::PlannedOnly
4883        );
4884        assert_eq!(checked.decision.compatibility_outcome().mutation_count, 0);
4885        assert_eq!(telemetry.observed_pass_count, 1);
4886        assert_eq!(telemetry.preserved_pass_count, 0);
4887        assert_eq!(telemetry.blocked_pass_count, 1);
4888    }
4889
4890    #[test]
4891    fn semantic_trust_is_observational_to_applied_decisions() {
4892        let outcome = TransformPassExecutionOutcomeV0 {
4893            pass_id: "empty-rule-removal",
4894            status: TransformPassRuntimeStatus::Applied,
4895            input_byte_len: 12,
4896            output_byte_len: 0,
4897            mutation_count: 1,
4898            provenance_preserved: true,
4899            detail: "removed an empty rule",
4900        };
4901        let without_trust = TransformDecisionDraftV0::Applied {
4902            outcome: outcome.clone(),
4903        }
4904        .finalize("input".to_string(), "output".to_string(), Vec::new(), None);
4905        let with_trust = TransformDecisionDraftV0::Applied { outcome }.finalize(
4906            "input".to_string(),
4907            "output".to_string(),
4908            Vec::new(),
4909            Some(TransformSemanticGuaranteeTierV0::L0Observed),
4910        );
4911
4912        assert_eq!(
4913            without_trust.compatibility_outcome(),
4914            with_trust.compatibility_outcome()
4915        );
4916        assert_eq!(
4917            without_trust.rollback_receipt(),
4918            with_trust.rollback_receipt()
4919        );
4920        assert!(without_trust.semantic_guarantee_tier().is_none());
4921        assert_eq!(
4922            with_trust.semantic_guarantee_tier(),
4923            Some(&TransformSemanticGuaranteeTierV0::L0Observed)
4924        );
4925    }
4926
4927    #[test]
4928    fn rollback_receipts_distinguish_committed_rewrites_from_rejected_transactions()
4929    -> Result<(), String> {
4930        let source = ".card { color: red; } .card { color: red; }";
4931        let summary =
4932            execute_transform_passes_on_source(source, &[TransformPassKind::RuleDeduplication]);
4933        let applied_receipt = summary
4934            .decisions
4935            .first()
4936            .and_then(TransformDecision::rollback_receipt)
4937            .ok_or_else(|| "applied rewrite should carry a rollback receipt".to_string())?;
4938        assert_eq!(
4939            applied_receipt.restorable,
4940            RollbackScopeV0::CommittedIrrecoverable
4941        );
4942        assert_eq!(applied_receipt.attempted_mutation_count, Some(1));
4943        assert_eq!(
4944            applied_receipt.input_content_signature,
4945            transform_content_signature(source)
4946        );
4947        assert!(applied_receipt.output_preserved_content_signature.is_none());
4948
4949        let rejected_source = ".card { color: tokens.$accent; }";
4950        let mut rejected_ir = lower_transform_ir_from_source(
4951            rejected_source,
4952            StyleDialect::Scss,
4953            "rollback-rejected-transaction",
4954        );
4955        let rule = rejected_ir
4956            .nodes
4957            .iter()
4958            .find(|node| node.kind == IrNodeKindV0::StyleRule)
4959            .map(|node| node.node_id)
4960            .ok_or_else(|| "rejection fixture should contain a style rule".to_string())?;
4961        let input_signature = transform_content_signature(rejected_ir.source_text());
4962        let region = IrEditRegionV0::full(rejected_ir.source_byte_len);
4963        let mut transaction =
4964            IrTransactionV0::new(&mut rejected_ir, "rollback-rejected-transaction", region);
4965        transaction
4966            .replace_node(rule, ".card { color: blue; }")
4967            .map_err(|error| format!("{error:?}"))?;
4968        assert!(transaction.commit().is_err());
4969        assert_eq!(rejected_ir.source_text(), rejected_source);
4970
4971        let rejected_decision = TransformDecisionDraftV0::Rejected {
4972            reason: TransformRejectionReasonV0::IrTransaction {
4973                pass: TransformPassKind::RuleDeduplication,
4974            },
4975            outcome: planned_only_outcome(
4976                TransformPassKind::RuleDeduplication.id(),
4977                rejected_source.len(),
4978                rejected_source.len(),
4979                "transaction preserved its input",
4980            ),
4981        }
4982        .finalize(
4983            input_signature,
4984            transform_content_signature(rejected_ir.source_text()),
4985            Vec::new(),
4986            None,
4987        );
4988        let rejected_receipt = rejected_decision
4989            .rollback_receipt()
4990            .ok_or_else(|| "rejected rewrite should carry a rollback receipt".to_string())?;
4991        assert!(rejected_receipt.preserves_rejected_input());
4992        assert_eq!(rejected_receipt.attempted_mutation_count, None);
4993
4994        let mut perturbed_receipt = rejected_receipt.clone();
4995        perturbed_receipt.output_preserved_content_signature =
4996            Some(transform_content_signature(".card { color: blue; }"));
4997        assert!(!perturbed_receipt.preserves_rejected_input());
4998
4999        let shadow =
5000            crate::runtime::structural_shadow::summarize_structural_ir_shadow_equivalence_v0();
5001        assert!(shadow.fixture_count > 0);
5002        assert!(shadow.all_fields_match);
5003        assert!(shadow.all_typed_path_fields_match);
5004        Ok(())
5005    }
5006
5007    #[test]
5008    fn semantic_preservation_counts_executed_simple_structural_pass() {
5009        let execution = execute_transform_passes_on_source(
5010            ".dup { color: red; }.dup { color: red; }",
5011            &[TransformPassKind::RuleDeduplication],
5012        );
5013
5014        assert_eq!(
5015            execution
5016                .semantic_preservation_telemetry
5017                .observed_pass_count,
5018            1
5019        );
5020        assert_eq!(
5021            execution
5022                .semantic_preservation_telemetry
5023                .preserved_pass_count,
5024            1
5025        );
5026        assert_eq!(
5027            execution.semantic_preservation_telemetry.blocked_pass_count,
5028            0
5029        );
5030    }
5031
5032    fn structural_dispatch_fixture_bundle() -> Result<ClosedWorldBundleV0, String> {
5033        let instance = ModuleInstanceKeyV0::new(
5034            ModuleIdV0::new("omena-transform-passes.structural-dispatch-fixture"),
5035            ConfigurationHashV0::none(),
5036        );
5037        let module = ClosedWorldLinkedModuleV0::new(instance.clone())
5038            .with_class_name("used")
5039            .with_class_name("button")
5040            .with_class_name("base")
5041            .with_class_name("utility")
5042            .with_keyframe_name("spin")
5043            .with_value_name("keepExport")
5044            .with_custom_property_name("keepExport");
5045        ClosedWorldBundleV0::try_from_linked_modules(vec![instance], vec![module])
5046            .map_err(|err| format!("structural dispatch fixture bundle should be valid: {err:?}"))
5047    }
5048
5049    #[test]
5050    fn structural_only_execution_never_records_lex_cache_full_relex_fallback() {
5051        let structural_passes = default_transform_pass_descriptors()
5052            .into_iter()
5053            .filter(|descriptor| descriptor.pass_class == TransformPassClassV0::Structural)
5054            .map(|descriptor| descriptor.kind)
5055            .collect::<Vec<_>>();
5056        let source =
5057            ".dup { color: red; }.dup { color: red; }.empty { } @media screen { .media { } }";
5058
5059        super::super::lex_cache::reset_transform_lex_cache_splice_telemetry();
5060        let execution = execute_transform_passes_on_source_with_dialect_and_context(
5061            source,
5062            StyleDialect::Css,
5063            structural_passes.as_slice(),
5064            &TransformExecutionContextV0::default(),
5065        );
5066        let telemetry = super::super::lex_cache::transform_lex_cache_splice_telemetry_snapshot();
5067
5068        assert_eq!(structural_passes.len(), 21);
5069        assert!(execution.mutation_count > 0);
5070        assert!(
5071            execution
5072                .structural_ir_transaction_telemetry
5073                .transaction_commit_count
5074                > 0
5075        );
5076        assert_eq!(telemetry.full_relex_fallback_count, 0);
5077        assert_eq!(telemetry.window_derivation_fallback_count, 0);
5078        assert_eq!(telemetry.full_output_window_fallback_count, 0);
5079        assert_eq!(telemetry.token_offset_fallback_count, 0);
5080    }
5081
5082    #[test]
5083    fn structural_execution_hashes_less_class_names_through_ir_transactions() {
5084        let context = TransformExecutionContextV0 {
5085            class_name_rewrites: vec![crate::TransformClassNameRewriteV0 {
5086                original_name: "button".to_string(),
5087                rewritten_name: "_button_x".to_string(),
5088            }],
5089            ..TransformExecutionContextV0::default()
5090        };
5091        let execution = execute_transform_passes_on_source_with_dialect_and_context(
5092            ".button { color: red; }",
5093            StyleDialect::Less,
5094            &[
5095                TransformPassKind::HashCssModuleClassNames,
5096                TransformPassKind::PrintCss,
5097            ],
5098            &context,
5099        );
5100
5101        assert_eq!(execution.output_css, "._button_x{ color: red; }");
5102        assert!(
5103            execution
5104                .structural_ir_transaction_telemetry
5105                .transaction_commit_count
5106                > 0
5107        );
5108    }
5109
5110    #[test]
5111    fn executor_loop_dispatches_without_pass_kind_match() -> Result<(), String> {
5112        let source = std::fs::read_to_string(
5113            std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
5114                .join("src")
5115                .join("runtime")
5116                .join("executor.rs"),
5117        )
5118        .map_err(|err| format!("executor source should be readable: {err:?}"))?;
5119        let loop_anchor = source
5120            .find("let mut dispatch_result =")
5121            .ok_or_else(|| "executor should keep a dispatch result boundary".to_string())?;
5122        let loop_match_tail = &source[loop_anchor..];
5123        let destructure_anchor = loop_match_tail
5124            .find("let TransformPassDispatchResultV0")
5125            .ok_or_else(|| "executor should destructure the dispatch result".to_string())?;
5126        let loop_dispatch_body = &loop_match_tail[..destructure_anchor];
5127
5128        assert!(loop_dispatch_body.contains("dispatch_text_local_pass"));
5129        assert!(loop_dispatch_body.contains("dispatch_module_evaluation_pass"));
5130        assert!(loop_dispatch_body.contains("dispatch_structural_pass"));
5131        assert!(loop_dispatch_body.contains("dispatch_emission_pass"));
5132        assert!(loop_dispatch_body.contains("TransformRuntimePassImplementationV0::Structural"));
5133        assert!(
5134            source.contains("runtime_pass_entry_for_kind(kind, pass_registry.entries.as_slice())")
5135        );
5136        assert!(!loop_dispatch_body.contains("ModuleEvaluationOrEgressHandler"));
5137        assert!(!loop_dispatch_body.contains("StructuralHandler"));
5138        assert!(!loop_dispatch_body.contains("match pass"));
5139        assert!(!loop_dispatch_body.contains("Some(TransformPassKind::"));
5140        Ok(())
5141    }
5142}
5143
5144#[cfg(test)]
5145mod module_evaluation_materialization_tests {
5146    use super::*;
5147    use crate::model::TransformModuleEvaluationOracleV0;
5148
5149    fn oracle_allowing_native_output() -> TransformModuleEvaluationOracleV0 {
5150        TransformModuleEvaluationOracleV0 {
5151            mode: "oracleOnly".to_string(),
5152            product_output_source: "legacyEvaluatedCss".to_string(),
5153            divergence_count: 0,
5154            all_legacy_declaration_values_preserved: true,
5155            ..TransformModuleEvaluationOracleV0::default()
5156        }
5157    }
5158
5159    fn module_evaluation(
5160        evaluated_css: &str,
5161        native_edit_output: Option<&str>,
5162        oracle: Option<TransformModuleEvaluationOracleV0>,
5163    ) -> TransformModuleEvaluationV0 {
5164        TransformModuleEvaluationV0 {
5165            evaluator: "test".to_string(),
5166            product_output_source: Some("nativeEditOutput".to_string()),
5167            evaluated_css: evaluated_css.to_string(),
5168            native_edit_output: native_edit_output.map(str::to_string),
5169            native_replacements: Vec::new(),
5170            native_edits: Vec::new(),
5171            oracle,
5172        }
5173    }
5174
5175    #[test]
5176    fn module_evaluation_consumes_oracle_backed_matching_native_output() {
5177        let input_css = ".input { color: red; }";
5178        let evaluation = module_evaluation(
5179            ".native { color: red; }",
5180            Some(".native { color: red; }"),
5181            Some(oracle_allowing_native_output()),
5182        );
5183
5184        let output = materialize_transform_module_evaluation_output(
5185            input_css,
5186            &evaluation,
5187            "native",
5188            "preserve",
5189        );
5190
5191        assert_eq!(output.css, ".native { color: red; }");
5192        assert_eq!(output.detail, "native");
5193    }
5194
5195    #[test]
5196    fn module_evaluation_preserves_input_when_oracle_backed_native_output_mismatches() {
5197        let input_css = ".input { color: red; }";
5198        let evaluation = module_evaluation(
5199            ".legacy { color: red; }",
5200            Some(".native { color: red; }"),
5201            Some(oracle_allowing_native_output()),
5202        );
5203
5204        let output = materialize_transform_module_evaluation_output(
5205            input_css,
5206            &evaluation,
5207            "native",
5208            "preserve",
5209        );
5210
5211        assert_eq!(output.css, input_css);
5212        assert_eq!(output.detail, "preserve");
5213    }
5214}
5215
5216#[cfg(test)]
5217mod coordinate_map_tests {
5218    use super::*;
5219
5220    fn mutation_span(
5221        source_span_start: usize,
5222        source_span_end: usize,
5223        generated_span_start: usize,
5224        generated_span_end: usize,
5225    ) -> TransformProvenanceMutationSpanV0 {
5226        TransformProvenanceMutationSpanV0 {
5227            source_span_start,
5228            source_span_end,
5229            generated_span_start,
5230            generated_span_end,
5231            node_key: None,
5232        }
5233    }
5234
5235    // After a length-changing pass, a span in the drifted (post-pass) coordinate space must
5236    // map back to the correct ORIGINAL-source interval. This is the multi-pass remap the
5237    // node_key coordinate caveat exists to solve; it is RED if `apply_mutation_spans` were
5238    // identity-only (it would return the un-remapped (5,7) instead of the original (3,5)).
5239    #[test]
5240    fn coordinate_map_remaps_post_mutation_span_to_original_after_one_pass() {
5241        // original "abcdef" (len 6); pass replaces current [1,3) ("bc") with 4 bytes -> generated [1,5).
5242        let mut map = TransformSpanCoordinateMapV0::new(6);
5243        map.apply_mutation_spans(&[mutation_span(1, 3, 1, 5)]);
5244        // post-pass output "a????def" (len 8); current [5,7) ("de") -> original [3,5) ("de").
5245        assert_eq!(map.map_current_span_to_original(5, 7), Some((3, 5)));
5246        // the surviving prefix still maps to itself.
5247        assert_eq!(map.map_current_span_to_original(0, 1), Some((0, 1)));
5248    }
5249
5250    // The remap composes across two stacked mutating passes (the case with zero prior coverage).
5251    #[test]
5252    fn coordinate_map_composes_across_two_mutating_passes() {
5253        let mut map = TransformSpanCoordinateMapV0::new(6); // "abcdef"
5254        // pass 1: current [1,3) -> 4 bytes (generated [1,5)); output len 8.
5255        map.apply_mutation_spans(&[mutation_span(1, 3, 1, 5)]);
5256        // pass 2 (coords in pass-1 output space, len 8): current [6,7) -> 3 bytes (generated [6,9)); output len 10.
5257        map.apply_mutation_spans(&[mutation_span(6, 7, 6, 9)]);
5258        // pass-2 output current [9,10) is the trailing original "f" at original [5,6).
5259        assert_eq!(map.map_current_span_to_original(9, 10), Some((5, 6)));
5260        assert_eq!(map.map_current_span_to_original(0, 1), Some((0, 1)));
5261    }
5262
5263    // node_key is best-effort: a post-mutation span straddling a prior mutation boundary
5264    // matches no single surviving segment and maps to None (omitted, never mis-attributed).
5265    #[test]
5266    fn coordinate_map_returns_none_for_post_mutation_straddling_span() {
5267        let mut map = TransformSpanCoordinateMapV0::new(6);
5268        map.apply_mutation_spans(&[mutation_span(1, 3, 1, 5)]);
5269        // current [0,7) straddles the surviving prefix [0,1) and the shifted suffix [5,8).
5270        assert_eq!(map.map_current_span_to_original(0, 7), None);
5271    }
5272}