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