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