Skip to main content

omena_transform_passes/runtime/
executor.rs

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