1use omena_cascade::DiagnosticFrameFootprintV0;
2use omena_incremental::select_frame_aware_recheck_set;
3use serde::Serialize;
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
6#[serde(rename_all = "camelCase")]
7pub struct FrameAwareRefreshReportV0 {
8 pub schema_version: &'static str,
9 pub product: &'static str,
10 pub feature_gate: &'static str,
11 pub selective_refresh_enabled: bool,
12 pub edited_module_count: usize,
13 pub selected_diagnostic_instance_ids: Vec<String>,
14 pub skipped_diagnostic_instance_ids: Vec<String>,
15 pub layer_marker: &'static str,
16}
17
18#[derive(Debug, Clone, PartialEq, Serialize)]
19#[serde(rename_all = "camelCase")]
20pub struct FrameAwareRefreshComparisonV0 {
21 pub schema_version: &'static str,
22 pub product: &'static str,
23 pub fixed_workspace_fixture: &'static str,
24 pub diagnostic_frame_count: usize,
25 pub edited_module_count: usize,
26 pub unconditional_selected_count: usize,
27 pub selective_selected_count: usize,
28 pub skipped_diagnostic_count: usize,
29 pub selected_work_reduction_count: usize,
30 pub selected_work_reduction_ratio: f64,
31 pub measured_latency_proxy: &'static str,
32 pub selective_refresh_enabled_by_default: bool,
33 pub feature_gate: &'static str,
34 pub disable_gate: &'static str,
35 pub layer_marker: &'static str,
36}
37
38pub fn refresh_diagnostics_with_frame(
39 frames: &[DiagnosticFrameFootprintV0],
40 edited_module_ids: Vec<String>,
41) -> FrameAwareRefreshReportV0 {
42 let selective_refresh_enabled = frame_aware_refresh_enabled_from_env();
43 refresh_diagnostics_with_frame_policy(frames, edited_module_ids, selective_refresh_enabled)
44}
45
46pub fn refresh_diagnostics_with_frame_policy(
47 frames: &[DiagnosticFrameFootprintV0],
48 edited_module_ids: Vec<String>,
49 selective_refresh_enabled: bool,
50) -> FrameAwareRefreshReportV0 {
51 let edited_module_count = edited_module_ids.len();
52 let selection = if selective_refresh_enabled {
53 select_frame_aware_recheck_set(frames, edited_module_ids)
54 } else {
55 select_frame_aware_recheck_set(frames, frames_to_module_ids(frames))
56 };
57
58 FrameAwareRefreshReportV0 {
59 schema_version: "0",
60 product: "omena-lsp-server.frame-aware-refresh",
61 feature_gate: "OMENA_LSP_ENABLE_FRAME_AWARE_REFRESH",
62 selective_refresh_enabled,
63 edited_module_count,
64 selected_diagnostic_instance_ids: selection.selected_diagnostic_instance_ids,
65 skipped_diagnostic_instance_ids: selection.skipped_diagnostic_instance_ids,
66 layer_marker: "frame-rule",
67 }
68}
69
70pub fn compare_frame_refresh_against_unconditional_baseline(
71 frames: &[DiagnosticFrameFootprintV0],
72 edited_module_ids: Vec<String>,
73) -> FrameAwareRefreshComparisonV0 {
74 let unconditional =
75 refresh_diagnostics_with_frame_policy(frames, edited_module_ids.clone(), false);
76 let selective = refresh_diagnostics_with_frame_policy(frames, edited_module_ids, true);
77 let unconditional_selected_count = unconditional.selected_diagnostic_instance_ids.len();
78 let selective_selected_count = selective.selected_diagnostic_instance_ids.len();
79 let selected_work_reduction_count =
80 unconditional_selected_count.saturating_sub(selective_selected_count);
81 let selected_work_reduction_ratio = if unconditional_selected_count == 0 {
82 0.0
83 } else {
84 selected_work_reduction_count as f64 / unconditional_selected_count as f64
85 };
86
87 FrameAwareRefreshComparisonV0 {
88 schema_version: "0",
89 product: "omena-lsp-server.frame-aware-refresh-comparison",
90 fixed_workspace_fixture: "m4-alpha-fixed-workspace-frame-refresh",
91 diagnostic_frame_count: frames.len(),
92 edited_module_count: unconditional.edited_module_count,
93 unconditional_selected_count,
94 selective_selected_count,
95 skipped_diagnostic_count: selective.skipped_diagnostic_instance_ids.len(),
96 selected_work_reduction_count,
97 selected_work_reduction_ratio,
98 measured_latency_proxy: "selected-diagnostic-work-count",
99 selective_refresh_enabled_by_default: frame_aware_refresh_enabled(false, false),
100 feature_gate: "OMENA_LSP_ENABLE_FRAME_AWARE_REFRESH",
101 disable_gate: "OMENA_LSP_DISABLE_FRAME_AWARE_REFRESH",
102 layer_marker: "frame-rule",
103 }
104}
105
106fn frames_to_module_ids(frames: &[DiagnosticFrameFootprintV0]) -> Vec<String> {
107 frames
108 .iter()
109 .flat_map(|frame| frame.evidence_module_ids.iter().cloned())
110 .collect()
111}
112
113fn frame_aware_refresh_enabled_from_env() -> bool {
114 frame_aware_refresh_enabled(
115 std::env::var_os("OMENA_LSP_ENABLE_FRAME_AWARE_REFRESH").is_some(),
116 std::env::var_os("OMENA_LSP_DISABLE_FRAME_AWARE_REFRESH").is_some(),
117 )
118}
119
120fn frame_aware_refresh_enabled(enable_requested: bool, disable_requested: bool) -> bool {
121 enable_requested && !disable_requested
122}
123
124#[cfg(test)]
125mod tests {
126 use omena_cascade::derive_frame_for_diagnostic;
127
128 use super::*;
129
130 #[test]
131 fn refresh_report_selects_only_intersecting_frames() {
132 let frame = derive_frame_for_diagnostic(
133 "missing-static-class",
134 "d1",
135 vec!["file:///workspace/a.module.css".to_string()],
136 );
137 let report = refresh_diagnostics_with_frame(
138 &[frame],
139 vec!["file:///workspace/a.module.css".to_string()],
140 );
141
142 assert_eq!(report.schema_version, "0");
143 assert_eq!(report.layer_marker, "frame-rule");
144 assert_eq!(report.feature_gate, "OMENA_LSP_ENABLE_FRAME_AWARE_REFRESH");
145 assert_eq!(report.selected_diagnostic_instance_ids, vec!["d1"]);
146 }
147
148 #[test]
149 fn refresh_report_preserves_full_refresh_when_disabled() {
150 let selected = derive_frame_for_diagnostic(
151 "missing-static-class",
152 "selected",
153 vec!["file:///workspace/a.module.css".to_string()],
154 );
155 let otherwise_skipped = derive_frame_for_diagnostic(
156 "missing-static-class",
157 "otherwise-skipped",
158 vec!["file:///workspace/b.module.css".to_string()],
159 );
160 let report = refresh_diagnostics_with_frame_policy(
161 &[selected, otherwise_skipped],
162 vec!["file:///workspace/a.module.css".to_string()],
163 false,
164 );
165
166 assert!(!report.selective_refresh_enabled);
167 assert_eq!(
168 report.selected_diagnostic_instance_ids,
169 vec!["selected", "otherwise-skipped"]
170 );
171 assert!(report.skipped_diagnostic_instance_ids.is_empty());
172 }
173
174 #[test]
175 fn refresh_policy_is_positive_opt_in() {
176 assert!(!frame_aware_refresh_enabled(false, false));
177 assert!(frame_aware_refresh_enabled(true, false));
178 assert!(!frame_aware_refresh_enabled(true, true));
179 assert!(!frame_aware_refresh_enabled(false, true));
180 }
181
182 #[test]
183 fn fixed_workspace_comparison_reports_work_reduction_when_enabled() {
184 let frames = (0..100)
185 .map(|index| {
186 derive_frame_for_diagnostic(
187 "missing-static-class",
188 format!("d{index}"),
189 vec![format!("file:///workspace/module-{index}.module.css")],
190 )
191 })
192 .collect::<Vec<_>>();
193 let report = compare_frame_refresh_against_unconditional_baseline(
194 &frames,
195 vec!["file:///workspace/module-7.module.css".to_string()],
196 );
197
198 assert_eq!(report.schema_version, "0");
199 assert_eq!(report.layer_marker, "frame-rule");
200 assert_eq!(report.feature_gate, "OMENA_LSP_ENABLE_FRAME_AWARE_REFRESH");
201 assert!(!report.selective_refresh_enabled_by_default);
202 assert_eq!(report.diagnostic_frame_count, 100);
203 assert_eq!(report.unconditional_selected_count, 100);
204 assert_eq!(report.selective_selected_count, 1);
205 assert_eq!(report.skipped_diagnostic_count, 99);
206 assert!(report.selected_work_reduction_ratio >= 0.99);
207 }
208}