Skip to main content

dsfb_semiconductor/
precursor.rs

1use crate::baselines::BaselineSet;
2use crate::error::{DsfbSemiconductorError, Result};
3use crate::grammar::{GrammarSet, GrammarState};
4use crate::heuristics::{
5    dsa_contributing_motif_names, heuristic_policy_definition, FeaturePolicyOverride,
6    HeuristicAlertClass, HeuristicPolicyDefinition,
7};
8use crate::nominal::NominalModel;
9use crate::preprocessing::PreparedDataset;
10use crate::residual::ResidualSet;
11use crate::semiotics::feature_semantic_flags;
12use crate::signs::SignSet;
13use serde::{Deserialize, Serialize};
14use std::collections::BTreeMap;
15
16#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
17pub struct DsaConfig {
18    pub window: usize,
19    pub persistence_runs: usize,
20    pub alert_tau: f64,
21    pub corroborating_feature_count_min: usize,
22}
23
24impl Default for DsaConfig {
25    fn default() -> Self {
26        Self {
27            window: 5,
28            persistence_runs: 2,
29            alert_tau: 2.0,
30            corroborating_feature_count_min: 2,
31        }
32    }
33}
34
35impl DsaConfig {
36    pub fn validate(&self) -> Result<()> {
37        if self.window == 0 {
38            return Err(DsfbSemiconductorError::DatasetFormat(
39                "dsa window must be positive".into(),
40            ));
41        }
42        if self.persistence_runs == 0 {
43            return Err(DsfbSemiconductorError::DatasetFormat(
44                "dsa persistence_runs must be positive".into(),
45            ));
46        }
47        if self.alert_tau <= 0.0 {
48            return Err(DsfbSemiconductorError::DatasetFormat(
49                "dsa alert_tau must be positive".into(),
50            ));
51        }
52        if self.corroborating_feature_count_min == 0 {
53            return Err(DsfbSemiconductorError::DatasetFormat(
54                "dsa corroborating_feature_count_min must be positive".into(),
55            ));
56        }
57        Ok(())
58    }
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
62pub struct DsaWeights {
63    pub boundary_density: f64,
64    pub drift_persistence: f64,
65    pub slew_density: f64,
66    pub ewma_occupancy: f64,
67    pub motif_recurrence: f64,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
71pub struct RecallRescueConfig {
72    pub enabled: bool,
73    pub priority_one_score_margin: f64,
74    pub priority_two_score_margin: f64,
75    pub priority_three_score_margin: f64,
76    pub minimum_ewma_occupancy: f64,
77    pub minimum_boundary_density: f64,
78    pub minimum_motif_recurrence: f64,
79}
80
81impl Default for RecallRescueConfig {
82    fn default() -> Self {
83        Self {
84            enabled: false,
85            priority_one_score_margin: 0.10,
86            priority_two_score_margin: 0.40,
87            priority_three_score_margin: 1.00,
88            minimum_ewma_occupancy: 0.65,
89            minimum_boundary_density: 0.40,
90            minimum_motif_recurrence: 0.40,
91        }
92    }
93}
94
95#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
96pub struct DsaPolicyRuntime {
97    pub feature_policy_overrides: Vec<FeaturePolicyOverride>,
98    pub recall_rescue: RecallRescueConfig,
99    pub semantic_rescue_support: BTreeMap<usize, Vec<bool>>,
100}
101
102impl Default for DsaWeights {
103    fn default() -> Self {
104        Self {
105            boundary_density: 1.0,
106            drift_persistence: 1.0,
107            slew_density: 1.0,
108            ewma_occupancy: 1.0,
109            motif_recurrence: 1.0,
110        }
111    }
112}
113
114#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
115pub enum DsaPolicyState {
116    Silent,
117    Watch,
118    Review,
119    Escalate,
120}
121
122impl DsaPolicyState {
123    fn is_review_or_escalate(self) -> bool {
124        matches!(self, Self::Review | Self::Escalate)
125    }
126
127    pub fn as_lowercase(self) -> &'static str {
128        match self {
129            Self::Silent => "silent",
130            Self::Watch => "watch",
131            Self::Review => "review",
132            Self::Escalate => "escalate",
133        }
134    }
135}
136
137#[derive(Debug, Clone, Serialize)]
138pub struct FeatureMotifPolicyContribution {
139    pub motif_name: String,
140    pub alert_class_default: HeuristicAlertClass,
141    pub watch_points: usize,
142    pub review_points: usize,
143    pub escalate_points: usize,
144    pub silent_suppression_points: usize,
145    pub pass_review_or_escalate_points: usize,
146    pub pre_failure_review_or_escalate_points: usize,
147}
148
149#[derive(Debug, Clone, Serialize)]
150pub struct DsaMotifPolicyContribution {
151    pub motif_name: String,
152    pub alert_class_default: HeuristicAlertClass,
153    pub watch_points: usize,
154    pub review_points: usize,
155    pub escalate_points: usize,
156    pub silent_suppression_points: usize,
157    pub pass_review_or_escalate_points: usize,
158    pub pre_failure_review_or_escalate_points: usize,
159}
160
161#[derive(Debug, Clone, Serialize)]
162pub struct DsaParameterManifest {
163    pub config: DsaConfig,
164    pub weights: DsaWeights,
165    pub feature_policy_override_count: usize,
166    pub feature_policy_override_summary: Vec<String>,
167    pub policy_engine_definition: String,
168    pub feature_level_state_definition: String,
169    pub primary_run_signal: String,
170    pub primary_run_signal_definition: String,
171    pub secondary_run_signal: String,
172    pub tertiary_run_signal: String,
173    pub strict_escalate_signal: String,
174    pub rolling_window_definition: String,
175    pub boundary_density_basis: String,
176    pub drift_persistence_definition: String,
177    pub slew_density_definition: String,
178    pub ewma_occupancy_formula: String,
179    pub motif_names_used_for_recurrence: Vec<String>,
180    pub directional_consistency_rule: String,
181    pub silence_rule: String,
182    pub corroboration_rule: String,
183    pub recall_rescue_definition: String,
184    pub recall_tolerance_runs_for_primary_success: usize,
185    pub primary_success_condition_definition: String,
186    pub optimization_priority_order: Vec<String>,
187}
188
189#[derive(Debug, Clone, Serialize)]
190pub struct DsaFeatureTrace {
191    pub feature_index: usize,
192    pub feature_name: String,
193    pub boundary_basis_hit: Vec<bool>,
194    pub drift_outward_hit: Vec<bool>,
195    pub slew_hit: Vec<bool>,
196    pub motif_hit: Vec<bool>,
197    pub boundary_density_w: Vec<f64>,
198    pub drift_persistence_w: Vec<f64>,
199    pub slew_density_w: Vec<f64>,
200    pub ewma_occupancy_w: Vec<f64>,
201    pub motif_recurrence_w: Vec<f64>,
202    pub fragmentation_proxy_w: Vec<f64>,
203    pub consistent: Vec<bool>,
204    pub dsa_score: Vec<f64>,
205    pub dsa_active: Vec<bool>,
206    pub numeric_dsa_alert: Vec<bool>,
207    pub dsa_alert: Vec<bool>,
208    pub resolved_alert_class: Vec<HeuristicAlertClass>,
209    pub policy_state: Vec<DsaPolicyState>,
210    pub policy_suppressed_to_silent: Vec<bool>,
211    pub rescue_transition: Vec<String>,
212    pub rescued_to_review: Vec<bool>,
213    pub motif_policy_contributions: Vec<FeatureMotifPolicyContribution>,
214}
215
216#[derive(Debug, Clone, Serialize)]
217pub struct DsaRunSignals {
218    pub primary_run_signal: String,
219    pub corroborating_feature_count_min: usize,
220    pub primary_run_alert: Vec<bool>,
221    pub any_feature_dsa_alert: Vec<bool>,
222    pub any_feature_raw_violation: Vec<bool>,
223    pub feature_count_dsa_alert: Vec<usize>,
224    pub watch_feature_count: Vec<usize>,
225    pub review_feature_count: Vec<usize>,
226    pub escalate_feature_count: Vec<usize>,
227    pub strict_escalate_run_alert: Vec<bool>,
228    pub numeric_primary_run_alert: Vec<bool>,
229    pub numeric_feature_count_dsa_alert: Vec<usize>,
230}
231
232#[derive(Debug, Clone, Serialize)]
233pub struct DsaEpisodeSummary {
234    pub primary_signal: String,
235    pub raw_boundary_episode_count: usize,
236    pub dsa_episode_count: usize,
237    pub dsa_episodes_preceding_failure: usize,
238    pub mean_dsa_episode_length_runs: Option<f64>,
239    pub max_dsa_episode_length_runs: usize,
240    pub compression_ratio: Option<f64>,
241    pub precursor_quality: Option<f64>,
242    pub non_escalating_dsa_episode_fraction: Option<f64>,
243}
244
245#[derive(Debug, Clone, Serialize)]
246pub struct DsaSignalSummary {
247    pub config: DsaConfig,
248    pub weights: DsaWeights,
249    pub primary_run_signal: String,
250    pub analyzable_feature_count: usize,
251    pub alert_point_count: usize,
252    pub alert_run_count: usize,
253    pub numeric_alert_point_count: usize,
254    pub numeric_alert_run_count: usize,
255    pub watch_point_count: usize,
256    pub review_point_count: usize,
257    pub escalate_point_count: usize,
258    pub silenced_point_count: usize,
259    pub rescued_point_count: usize,
260    pub rescued_watch_to_review_points: usize,
261    pub rescued_review_to_escalate_points: usize,
262    pub failure_runs: usize,
263    pub failure_run_recall: usize,
264    pub failure_run_recall_rate: f64,
265    pub numeric_primary_failure_run_recall: usize,
266    pub mean_lead_time_runs: Option<f64>,
267    pub median_lead_time_runs: Option<f64>,
268    pub pass_run_nuisance_proxy: f64,
269    pub numeric_primary_pass_run_nuisance_proxy: f64,
270    pub mean_lead_delta_vs_cusum_runs: Option<f64>,
271    pub mean_lead_delta_vs_run_energy_runs: Option<f64>,
272    pub mean_lead_delta_vs_pca_fdc_runs: Option<f64>,
273    pub mean_lead_delta_vs_threshold_runs: Option<f64>,
274    pub mean_lead_delta_vs_ewma_runs: Option<f64>,
275    pub raw_boundary_nuisance_proxy: f64,
276    pub raw_boundary_episode_count: usize,
277    pub dsa_episode_count: usize,
278    pub dsa_episodes_preceding_failure: usize,
279    pub mean_dsa_episode_length_runs: Option<f64>,
280    pub max_dsa_episode_length_runs: usize,
281    pub compression_ratio: Option<f64>,
282    pub precursor_quality: Option<f64>,
283    pub non_escalating_dsa_episode_fraction: Option<f64>,
284    pub threshold_recall_gate_passed: bool,
285    pub boundary_nuisance_gate_passed: bool,
286    pub primary_success_condition_met: bool,
287    pub any_metric_improved: bool,
288    pub validation_passed: bool,
289    pub success_condition_failures: Vec<String>,
290    pub validation_failures: Vec<String>,
291}
292
293#[derive(Debug, Clone, Serialize)]
294pub struct SignalComparisonRow {
295    pub signal: String,
296    pub failure_run_recall: usize,
297    pub failure_runs: usize,
298    pub failure_run_recall_rate: f64,
299    pub mean_lead_time_runs: Option<f64>,
300    pub median_lead_time_runs: Option<f64>,
301    pub pass_run_nuisance_proxy: f64,
302    pub mean_lead_delta_vs_cusum_runs: Option<f64>,
303    pub mean_lead_delta_vs_run_energy_runs: Option<f64>,
304    pub mean_lead_delta_vs_pca_fdc_runs: Option<f64>,
305    pub mean_lead_delta_vs_threshold_runs: Option<f64>,
306    pub mean_lead_delta_vs_ewma_runs: Option<f64>,
307}
308
309#[derive(Debug, Clone, Serialize)]
310pub struct DsaComponentContribution {
311    pub component: String,
312    pub mean_value_on_alert_points: f64,
313    pub mean_value_on_all_points: f64,
314    pub total_value_on_alert_points: f64,
315}
316
317#[derive(Debug, Clone, Serialize)]
318pub struct DsaVsBaselinesSummary {
319    pub dataset: String,
320    pub primary_run_signal: String,
321    pub dsa: SignalComparisonRow,
322    pub numeric_dsa: SignalComparisonRow,
323    pub threshold: SignalComparisonRow,
324    pub ewma: SignalComparisonRow,
325    pub cusum: SignalComparisonRow,
326    pub run_energy: SignalComparisonRow,
327    pub pca_fdc: SignalComparisonRow,
328    pub dsfb_violation: SignalComparisonRow,
329    pub dsfb_raw_boundary: SignalComparisonRow,
330    pub episode_summary: DsaEpisodeSummary,
331    pub failure_recall_delta_vs_threshold: i64,
332    pub failure_recall_delta_vs_ewma: i64,
333    pub failure_recall_delta_vs_cusum: i64,
334    pub failure_recall_delta_vs_run_energy: i64,
335    pub failure_recall_delta_vs_pca_fdc: i64,
336    pub failure_recall_delta_vs_violation: i64,
337    pub pass_run_nuisance_delta_vs_threshold: f64,
338    pub pass_run_nuisance_delta_vs_ewma: f64,
339    pub pass_run_nuisance_delta_vs_violation: f64,
340    pub pass_run_nuisance_delta_vs_cusum: f64,
341    pub pass_run_nuisance_delta_vs_run_energy: f64,
342    pub pass_run_nuisance_delta_vs_pca_fdc: f64,
343    pub pass_run_nuisance_delta_vs_raw_boundary: f64,
344    pub pass_run_nuisance_delta_vs_numeric_dsa: f64,
345    pub precursor_quality: Option<f64>,
346    pub dsa_episodes_preceding_failure: usize,
347    pub component_contributions: Vec<DsaComponentContribution>,
348    pub motif_policy_contributions: Vec<DsaMotifPolicyContribution>,
349    pub policy_vs_numeric_recall_delta: i64,
350    pub watch_point_count: usize,
351    pub review_point_count: usize,
352    pub escalate_point_count: usize,
353    pub silenced_point_count: usize,
354    pub nuisance_improved: bool,
355    pub lead_time_improved: bool,
356    pub recall_preserved: bool,
357    pub compression_improved: bool,
358    pub nothing_improved: bool,
359    pub threshold_recall_gate_passed: bool,
360    pub boundary_nuisance_gate_passed: bool,
361    pub primary_success_condition_met: bool,
362    pub any_metric_improved: bool,
363    pub validation_passed: bool,
364    pub success_condition_failures: Vec<String>,
365    pub validation_failures: Vec<String>,
366    pub conclusion: String,
367}
368
369#[derive(Debug, Clone, Serialize)]
370pub struct PerFailureRunDsaSignal {
371    pub failure_run_index: usize,
372    pub failure_timestamp: String,
373    pub earliest_dsa_run: Option<usize>,
374    pub earliest_primary_source: Option<String>,
375    pub earliest_dsa_feature_index: Option<usize>,
376    pub earliest_dsa_feature_name: Option<String>,
377    pub dsa_lead_runs: Option<usize>,
378    pub threshold_lead_runs: Option<usize>,
379    pub ewma_lead_runs: Option<usize>,
380    pub cusum_lead_runs: Option<usize>,
381    pub run_energy_lead_runs: Option<usize>,
382    pub pca_fdc_lead_runs: Option<usize>,
383    pub dsa_minus_cusum_delta_runs: Option<i64>,
384    pub dsa_minus_run_energy_delta_runs: Option<i64>,
385    pub dsa_minus_pca_fdc_delta_runs: Option<i64>,
386    pub dsa_minus_threshold_delta_runs: Option<i64>,
387    pub dsa_minus_ewma_delta_runs: Option<i64>,
388    pub dsa_alerting_feature_count: usize,
389    pub max_dsa_score_in_lookback: Option<f64>,
390    pub max_dsa_score_feature_index: Option<usize>,
391    pub max_dsa_score_feature_name: Option<String>,
392    pub max_dsa_score_run_index: Option<usize>,
393    pub max_dsa_score_boundary_density_w: Option<f64>,
394    pub max_dsa_score_drift_persistence_w: Option<f64>,
395    pub max_dsa_score_slew_density_w: Option<f64>,
396    pub max_dsa_score_ewma_occupancy_w: Option<f64>,
397    pub max_dsa_score_motif_recurrence_w: Option<f64>,
398    pub max_dsa_score_fragmentation_proxy_w: Option<f64>,
399    pub max_dsa_score_consistent: Option<bool>,
400    pub max_dsa_score_policy_state: Option<String>,
401    pub max_dsa_score_resolved_alert_class: Option<String>,
402    pub max_dsa_score_numeric_dsa_alert: Option<bool>,
403    pub max_dsa_score_dsa_alert: Option<bool>,
404    pub max_dsa_score_policy_suppressed: Option<bool>,
405    pub max_dsa_score_rescue_transition: Option<String>,
406}
407
408#[derive(Debug, Clone, Serialize)]
409pub struct DsaEvaluation {
410    pub traces: Vec<DsaFeatureTrace>,
411    pub run_signals: DsaRunSignals,
412    pub episode_summary: DsaEpisodeSummary,
413    pub parameter_manifest: DsaParameterManifest,
414    pub policy_runtime: DsaPolicyRuntime,
415    pub summary: DsaSignalSummary,
416    pub comparison_summary: DsaVsBaselinesSummary,
417    pub motif_policy_contributions: Vec<DsaMotifPolicyContribution>,
418    pub per_failure_run_signals: Vec<PerFailureRunDsaSignal>,
419}
420
421#[derive(Debug, Clone, Serialize, Deserialize)]
422pub struct DsaCalibrationGrid {
423    pub window: Vec<usize>,
424    pub persistence_runs: Vec<usize>,
425    pub alert_tau: Vec<f64>,
426    pub corroborating_feature_count_min: Vec<usize>,
427}
428
429impl DsaCalibrationGrid {
430    pub fn bounded_default() -> Self {
431        Self {
432            window: vec![5, 10, 15],
433            persistence_runs: vec![2, 3, 4],
434            alert_tau: vec![2.0, 2.5, 3.0],
435            corroborating_feature_count_min: vec![2, 3, 5],
436        }
437    }
438
439    pub fn validate(&self) -> Result<()> {
440        if self.grid_point_count() == 0 {
441            return Err(DsfbSemiconductorError::DatasetFormat(
442                "dsa calibration grid must contain at least one point".into(),
443            ));
444        }
445        if self.grid_point_count() > 256 {
446            return Err(DsfbSemiconductorError::DatasetFormat(format!(
447                "dsa calibration grid is too large ({})",
448                self.grid_point_count()
449            )));
450        }
451        Ok(())
452    }
453
454    pub fn grid_point_count(&self) -> usize {
455        [
456            self.window.len(),
457            self.persistence_runs.len(),
458            self.alert_tau.len(),
459            self.corroborating_feature_count_min.len(),
460        ]
461        .into_iter()
462        .product()
463    }
464
465    pub fn expand(&self) -> Vec<DsaConfig> {
466        let mut out = Vec::with_capacity(self.grid_point_count());
467        for &window in &self.window {
468            for &persistence_runs in &self.persistence_runs {
469                for &alert_tau in &self.alert_tau {
470                    for &corroborating_feature_count_min in &self.corroborating_feature_count_min {
471                        out.push(DsaConfig {
472                            window,
473                            persistence_runs,
474                            alert_tau,
475                            corroborating_feature_count_min,
476                        });
477                    }
478                }
479            }
480        }
481        out
482    }
483}
484
485#[derive(Debug, Clone, Serialize)]
486pub struct DsaCalibrationRow {
487    pub config_id: usize,
488    pub primary_run_signal: String,
489    pub window: usize,
490    pub persistence_runs: usize,
491    pub alert_tau: f64,
492    pub corroborating_feature_count_min: usize,
493    pub failure_run_recall: usize,
494    pub failure_runs: usize,
495    pub threshold_failure_run_recall: usize,
496    pub ewma_failure_run_recall: usize,
497    pub failure_recall_delta_vs_threshold: i64,
498    pub failure_recall_delta_vs_ewma: i64,
499    pub mean_lead_time_runs: Option<f64>,
500    pub median_lead_time_runs: Option<f64>,
501    pub pass_run_nuisance_proxy: f64,
502    pub mean_lead_delta_vs_cusum_runs: Option<f64>,
503    pub mean_lead_delta_vs_run_energy_runs: Option<f64>,
504    pub mean_lead_delta_vs_pca_fdc_runs: Option<f64>,
505    pub mean_lead_delta_vs_threshold_runs: Option<f64>,
506    pub mean_lead_delta_vs_ewma_runs: Option<f64>,
507    pub pass_run_nuisance_delta_vs_cusum: f64,
508    pub pass_run_nuisance_delta_vs_run_energy: f64,
509    pub pass_run_nuisance_delta_vs_pca_fdc: f64,
510    pub pass_run_nuisance_delta_vs_threshold: f64,
511    pub pass_run_nuisance_delta_vs_ewma: f64,
512    pub pass_run_nuisance_delta_vs_raw_boundary: f64,
513    pub raw_boundary_episode_count: usize,
514    pub dsa_episode_count: usize,
515    pub dsa_episodes_preceding_failure: usize,
516    pub mean_dsa_episode_length_runs: Option<f64>,
517    pub max_dsa_episode_length_runs: usize,
518    pub compression_ratio: Option<f64>,
519    pub precursor_quality: Option<f64>,
520    pub non_escalating_dsa_episode_fraction: Option<f64>,
521    pub nuisance_improved: bool,
522    pub lead_time_improved: bool,
523    pub recall_preserved: bool,
524    pub compression_improved: bool,
525    pub any_metric_improved: bool,
526    pub nothing_improved: bool,
527    pub threshold_recall_gate_passed: bool,
528    pub boundary_nuisance_gate_passed: bool,
529    pub primary_success_condition_met: bool,
530    pub validation_passed: bool,
531    pub success_condition_failures: String,
532    pub validation_failures: String,
533}
534
535#[derive(Debug, Clone, Serialize)]
536pub struct DsaCorroborationSummary {
537    pub corroborating_feature_count_min: usize,
538    pub representative_row: Option<DsaCalibrationRow>,
539}
540
541#[derive(Debug, Clone, Serialize)]
542pub struct DsaGridSummary {
543    pub grid_point_count: usize,
544    pub optimization_priority_order: Vec<String>,
545    pub primary_success_condition_definition: String,
546    pub success_row_count: usize,
547    pub any_success_row: bool,
548    pub closest_to_success: Option<DsaCalibrationRow>,
549    pub best_success_row: Option<DsaCalibrationRow>,
550    pub best_precursor_quality_row: Option<DsaCalibrationRow>,
551    pub corroboration_summaries: Vec<DsaCorroborationSummary>,
552    pub cross_feature_corroboration_effect: String,
553    pub limiting_factor: String,
554}
555
556const DSA_PRIMARY_SUCCESS_RECALL_TOLERANCE_RUNS: usize = 1;
557const POLICY_LOCAL_EWMA_CORROBORATION_MIN: f64 = 0.75;
558
559#[derive(Debug, Clone)]
560struct MotifContributionState {
561    motif_name: &'static str,
562    default_alert_class: HeuristicAlertClass,
563    contribution_state: DsaPolicyState,
564    fragmentation_proxy: f64,
565    suppressed_to_silent: bool,
566}
567
568fn dsa_optimization_priority_order() -> Vec<String> {
569    vec![
570        "1. Reduce nuisance vs raw DSFB boundary".into(),
571        "2. Reduce nuisance vs EWMA".into(),
572        "3. Preserve threshold recall".into(),
573        "4. Improve lead time without sacrificing nuisance".into(),
574    ]
575}
576
577fn dsa_primary_success_condition_definition() -> String {
578    format!(
579        "DSA is considered successful only if pass-run nuisance is lower than EWMA nuisance and failure recall is within {} run(s) of threshold recall.",
580        DSA_PRIMARY_SUCCESS_RECALL_TOLERANCE_RUNS
581    )
582}
583
584pub fn evaluate_dsa(
585    dataset: &PreparedDataset,
586    nominal: &NominalModel,
587    residuals: &ResidualSet,
588    signs: &SignSet,
589    baselines: &BaselineSet,
590    grammar: &GrammarSet,
591    config: &DsaConfig,
592    pre_failure_lookback_runs: usize,
593) -> Result<DsaEvaluation> {
594    evaluate_dsa_with_policy(
595        dataset,
596        nominal,
597        residuals,
598        signs,
599        baselines,
600        grammar,
601        config,
602        pre_failure_lookback_runs,
603        &DsaPolicyRuntime::default(),
604    )
605}
606
607pub fn evaluate_dsa_with_policy(
608    dataset: &PreparedDataset,
609    nominal: &NominalModel,
610    residuals: &ResidualSet,
611    signs: &SignSet,
612    baselines: &BaselineSet,
613    grammar: &GrammarSet,
614    config: &DsaConfig,
615    pre_failure_lookback_runs: usize,
616    policy_runtime: &DsaPolicyRuntime,
617) -> Result<DsaEvaluation> {
618    config.validate()?;
619    let weights = DsaWeights::default();
620    let run_count = dataset.labels.len();
621    let failure_indices = dataset
622        .labels
623        .iter()
624        .enumerate()
625        .filter_map(|(index, label)| (*label == 1).then_some(index))
626        .collect::<Vec<_>>();
627    let failure_window_mask =
628        build_failure_window_mask(run_count, &failure_indices, pre_failure_lookback_runs);
629    let feature_policy_overrides = policy_runtime
630        .feature_policy_overrides
631        .iter()
632        .map(|override_entry| (override_entry.feature_index, override_entry))
633        .collect::<BTreeMap<_, _>>();
634    let mut traces = Vec::with_capacity(residuals.traces.len());
635
636    for (((residual_trace, sign_trace), ewma_trace), grammar_trace) in residuals
637        .traces
638        .iter()
639        .zip(&signs.traces)
640        .zip(&baselines.ewma)
641        .zip(&grammar.traces)
642    {
643        let feature = &nominal.features[residual_trace.feature_index];
644        let feature_override = feature_policy_overrides
645            .get(&feature.feature_index)
646            .copied();
647        let semantic_rescue_support = policy_runtime
648            .semantic_rescue_support
649            .get(&feature.feature_index)
650            .cloned()
651            .unwrap_or_else(|| vec![false; run_count]);
652        if !feature.analyzable {
653            traces.push(empty_trace(
654                feature.feature_index,
655                &feature.feature_name,
656                run_count,
657            ));
658            continue;
659        }
660
661        let boundary_basis_hit = grammar_trace
662            .raw_states
663            .iter()
664            .map(|state| *state == GrammarState::Boundary)
665            .collect::<Vec<_>>();
666        let raw_violation_hit = grammar_trace
667            .raw_states
668            .iter()
669            .map(|state| *state == GrammarState::Violation)
670            .collect::<Vec<_>>();
671        let drift_outward_hit = sign_trace
672            .drift
673            .iter()
674            .map(|drift| *drift >= sign_trace.drift_threshold)
675            .collect::<Vec<_>>();
676        let slew_hit = sign_trace
677            .slew
678            .iter()
679            .map(|slew| slew.abs() >= sign_trace.slew_threshold)
680            .collect::<Vec<_>>();
681        let semantic_flags =
682            feature_semantic_flags(residual_trace, sign_trace, grammar_trace, feature.rho);
683        let motif_flags = dsa_contributing_motif_names()
684            .iter()
685            .map(|&motif_name| {
686                let flags = semantic_flags
687                    .semantic_flags
688                    .get(motif_name)
689                    .cloned()
690                    .unwrap_or_else(|| vec![false; run_count]);
691                (motif_name, flags)
692            })
693            .collect::<Vec<_>>();
694        let motif_hit = semantic_flags.any_semantic_match;
695        let ewma_normalized = ewma_trace
696            .ewma
697            .iter()
698            .map(|value| normalize_to_threshold(*value, ewma_trace.threshold))
699            .collect::<Vec<_>>();
700
701        let boundary_prefix = bool_prefix_sum(&boundary_basis_hit);
702        let raw_violation_prefix = bool_prefix_sum(&raw_violation_hit);
703        let drift_prefix = bool_prefix_sum(&drift_outward_hit);
704        let slew_prefix = bool_prefix_sum(&slew_hit);
705        let motif_prefix = bool_prefix_sum(&motif_hit);
706        let non_imputed_prefix = bool_prefix_sum(
707            &residual_trace
708                .is_imputed
709                .iter()
710                .map(|is_imputed| !*is_imputed)
711                .collect::<Vec<_>>(),
712        );
713
714        let mut boundary_density_w = Vec::with_capacity(run_count);
715        let mut drift_persistence_w = Vec::with_capacity(run_count);
716        let mut slew_density_w = Vec::with_capacity(run_count);
717        let mut ewma_occupancy_w = Vec::with_capacity(run_count);
718        let mut motif_recurrence_w = Vec::with_capacity(run_count);
719        let mut consistent = Vec::with_capacity(run_count);
720        let mut dsa_score = Vec::with_capacity(run_count);
721        let mut dsa_active = Vec::with_capacity(run_count);
722
723        for run_index in 0..run_count {
724            let start = run_index.saturating_sub(config.window.saturating_sub(1));
725            let window_len = (run_index - start + 1) as f64;
726            let boundary_density =
727                window_fraction_nonimputed(&boundary_prefix, &non_imputed_prefix, start, run_index);
728            let drift_persistence =
729                window_fraction_nonimputed(&drift_prefix, &non_imputed_prefix, start, run_index);
730            let slew_density = window_fraction(&slew_prefix, start, run_index, window_len);
731            let ewma_occupancy = window_mean(&ewma_normalized, start, run_index);
732            let motif_recurrence = window_fraction(&motif_prefix, start, run_index, window_len);
733            let consistent_window = window_is_consistent(
734                &sign_trace.drift,
735                sign_trace.drift_threshold,
736                start,
737                run_index,
738            );
739            let score = weights.boundary_density * boundary_density
740                + weights.drift_persistence * drift_persistence
741                + weights.slew_density * slew_density
742                + weights.ewma_occupancy * ewma_occupancy
743                + weights.motif_recurrence * motif_recurrence;
744
745            boundary_density_w.push(boundary_density);
746            drift_persistence_w.push(drift_persistence);
747            slew_density_w.push(slew_density);
748            ewma_occupancy_w.push(ewma_occupancy);
749            motif_recurrence_w.push(motif_recurrence);
750            consistent.push(consistent_window);
751            dsa_score.push(score);
752            dsa_active.push(score >= config.alert_tau && consistent_window);
753        }
754
755        let numeric_dsa_alert = persistence_mask(&dsa_active, config.persistence_runs);
756        let mut dsa_alert = Vec::with_capacity(run_count);
757        let mut fragmentation_proxy_w = Vec::with_capacity(run_count);
758        let mut resolved_alert_class = Vec::with_capacity(run_count);
759        let mut policy_state = Vec::with_capacity(run_count);
760        let mut policy_suppressed_to_silent = Vec::with_capacity(run_count);
761        let mut rescue_transition = Vec::with_capacity(run_count);
762        let mut rescued_to_review = Vec::with_capacity(run_count);
763        let mut motif_policy_contributions = dsa_contributing_motif_names()
764            .iter()
765            .map(|motif_name| {
766                let policy = heuristic_policy_definition(motif_name)
767                    .unwrap_or_else(|| panic!("missing heuristic policy for {motif_name}"));
768                (
769                    (*motif_name).to_string(),
770                    FeatureMotifPolicyContribution {
771                        motif_name: (*motif_name).into(),
772                        alert_class_default: policy.alert_class_default,
773                        watch_points: 0,
774                        review_points: 0,
775                        escalate_points: 0,
776                        silent_suppression_points: 0,
777                        pass_review_or_escalate_points: 0,
778                        pre_failure_review_or_escalate_points: 0,
779                    },
780                )
781            })
782            .collect::<BTreeMap<_, _>>();
783
784        for run_index in 0..run_count {
785            let start = run_index.saturating_sub(config.window.saturating_sub(1));
786            let raw_violation_recent = window_count(&raw_violation_prefix, start, run_index) > 0;
787            let local_corroboration = raw_violation_recent
788                || ewma_occupancy_w[run_index] >= POLICY_LOCAL_EWMA_CORROBORATION_MIN;
789            let contributions = motif_flags
790                .iter()
791                .filter_map(|(motif_name, flags)| {
792                    let policy = heuristic_policy_definition(motif_name)?;
793                    let contribution = motif_contribution_state(
794                        policy,
795                        feature_override,
796                        flags,
797                        run_index,
798                        dsa_active[run_index],
799                        numeric_dsa_alert[run_index],
800                        local_corroboration,
801                        raw_violation_recent,
802                    )?;
803                    Some(contribution)
804                })
805                .collect::<Vec<_>>();
806
807            let dominant_class = dominant_alert_class(&contributions);
808            let dominant_state = dominant_policy_state(&contributions);
809            let fragmentation = contributions
810                .iter()
811                .map(|contribution| contribution.fragmentation_proxy)
812                .fold(0.0, f64::max);
813            let silenced =
814                numeric_dsa_alert[run_index] && matches!(dominant_state, DsaPolicyState::Silent);
815
816            for contribution in &contributions {
817                if let Some(row) = motif_policy_contributions.get_mut(contribution.motif_name) {
818                    match contribution.contribution_state {
819                        DsaPolicyState::Silent => {
820                            if contribution.suppressed_to_silent {
821                                row.silent_suppression_points += 1;
822                            }
823                        }
824                        DsaPolicyState::Watch => row.watch_points += 1,
825                        DsaPolicyState::Review => row.review_points += 1,
826                        DsaPolicyState::Escalate => row.escalate_points += 1,
827                    }
828                    if contribution.contribution_state.is_review_or_escalate() {
829                        if dataset.labels[run_index] == -1 {
830                            row.pass_review_or_escalate_points += 1;
831                        }
832                        if failure_window_mask[run_index] {
833                            row.pre_failure_review_or_escalate_points += 1;
834                        }
835                    }
836                }
837            }
838
839            dsa_alert.push(dominant_state.is_review_or_escalate());
840            fragmentation_proxy_w.push(fragmentation);
841            resolved_alert_class.push(dominant_class);
842            policy_state.push(dominant_state);
843            policy_suppressed_to_silent.push(silenced);
844            rescue_transition.push("none".into());
845            rescued_to_review.push(false);
846        }
847
848        if policy_runtime.recall_rescue.enabled {
849            for run_index in 0..run_count {
850                let transition = apply_recall_rescue(
851                    feature_override,
852                    &policy_runtime.recall_rescue,
853                    config,
854                    &resolved_alert_class,
855                    &mut policy_state,
856                    &mut dsa_alert,
857                    &semantic_rescue_support,
858                    &fragmentation_proxy_w,
859                    &dsa_score,
860                    &boundary_density_w,
861                    &ewma_occupancy_w,
862                    &motif_recurrence_w,
863                    &consistent,
864                    run_index,
865                );
866                if let Some(transition_name) = transition {
867                    rescue_transition[run_index] = transition_name.to_string();
868                    rescued_to_review[run_index] = transition_name == "watch_to_review"
869                        || transition_name == "silent_to_review";
870                }
871            }
872        }
873
874        traces.push(DsaFeatureTrace {
875            feature_index: feature.feature_index,
876            feature_name: feature.feature_name.clone(),
877            boundary_basis_hit,
878            drift_outward_hit,
879            slew_hit,
880            motif_hit,
881            boundary_density_w,
882            drift_persistence_w,
883            slew_density_w,
884            ewma_occupancy_w,
885            motif_recurrence_w,
886            fragmentation_proxy_w,
887            consistent,
888            dsa_score,
889            dsa_active,
890            numeric_dsa_alert,
891            dsa_alert,
892            resolved_alert_class,
893            policy_state,
894            policy_suppressed_to_silent,
895            rescue_transition,
896            rescued_to_review,
897            motif_policy_contributions: motif_policy_contributions.into_values().collect(),
898        });
899    }
900
901    Ok(assemble_dsa_evaluation(
902        dataset,
903        nominal,
904        residuals,
905        baselines,
906        grammar,
907        traces,
908        config,
909        &weights,
910        pre_failure_lookback_runs,
911        policy_runtime,
912        None,
913        None,
914    ))
915}
916
917pub fn project_dsa_to_cohort(
918    dataset: &PreparedDataset,
919    nominal: &NominalModel,
920    residuals: &ResidualSet,
921    baselines: &BaselineSet,
922    grammar: &GrammarSet,
923    base_evaluation: &DsaEvaluation,
924    selected_feature_indices: &[usize],
925    corroborating_feature_count_min: usize,
926    pre_failure_lookback_runs: usize,
927    cohort_name: &str,
928) -> Result<DsaEvaluation> {
929    if corroborating_feature_count_min == 0 {
930        return Err(DsfbSemiconductorError::DatasetFormat(
931            "cohort corroborating_feature_count_min must be positive".into(),
932        ));
933    }
934
935    let mut selected_mask = vec![false; base_evaluation.traces.len()];
936    for &feature_index in selected_feature_indices {
937        if feature_index < selected_mask.len() {
938            selected_mask[feature_index] = true;
939        }
940    }
941
942    let mut traces = base_evaluation.traces.clone();
943    for (feature_index, trace) in traces.iter_mut().enumerate() {
944        if !selected_mask[feature_index] {
945            trace.dsa_score.fill(0.0);
946            trace.dsa_active.fill(false);
947            trace.numeric_dsa_alert.fill(false);
948            trace.dsa_alert.fill(false);
949            trace.resolved_alert_class.fill(HeuristicAlertClass::Silent);
950            trace.policy_state.fill(DsaPolicyState::Silent);
951            trace.policy_suppressed_to_silent.fill(false);
952            for contribution in &mut trace.motif_policy_contributions {
953                contribution.watch_points = 0;
954                contribution.review_points = 0;
955                contribution.escalate_points = 0;
956                contribution.silent_suppression_points = 0;
957                contribution.pass_review_or_escalate_points = 0;
958                contribution.pre_failure_review_or_escalate_points = 0;
959            }
960        }
961    }
962
963    let mut config = base_evaluation.parameter_manifest.config.clone();
964    config.corroborating_feature_count_min = corroborating_feature_count_min;
965    config.validate()?;
966
967    let mut evaluation = assemble_dsa_evaluation(
968        dataset,
969        nominal,
970        residuals,
971        baselines,
972        grammar,
973        traces,
974        &config,
975        &base_evaluation.parameter_manifest.weights,
976        pre_failure_lookback_runs,
977        &base_evaluation.policy_runtime,
978        Some(&selected_mask),
979        Some(selected_feature_indices.len()),
980    );
981
982    let primary_signal = format!(
983        "cohort {}: feature_count_review_or_escalate(k) >= {}",
984        cohort_name, corroborating_feature_count_min
985    );
986    evaluation.run_signals.primary_run_signal = primary_signal.clone();
987    evaluation.episode_summary.primary_signal = primary_signal.clone();
988    evaluation.summary.primary_run_signal = primary_signal.clone();
989    evaluation.summary.config.corroborating_feature_count_min = corroborating_feature_count_min;
990    evaluation.comparison_summary.primary_run_signal = primary_signal.clone();
991    evaluation
992        .parameter_manifest
993        .config
994        .corroborating_feature_count_min = corroborating_feature_count_min;
995    evaluation.parameter_manifest.primary_run_signal = primary_signal.clone();
996    evaluation.parameter_manifest.primary_run_signal_definition = format!(
997        "Primary run-level cohort DSA decision is {} over the deterministic selected feature cohort.",
998        primary_signal
999    );
1000    evaluation.parameter_manifest.corroboration_rule = format!(
1001        "RUN_LEVEL_DSA(k) is true only when the count of selected cohort features in Review or Escalate is at least {}.",
1002        corroborating_feature_count_min
1003    );
1004
1005    Ok(evaluation)
1006}
1007
1008pub fn run_dsa_calibration_grid(
1009    dataset: &PreparedDataset,
1010    nominal: &NominalModel,
1011    residuals: &ResidualSet,
1012    signs: &SignSet,
1013    baselines: &BaselineSet,
1014    grammar: &GrammarSet,
1015    grid: &DsaCalibrationGrid,
1016    pre_failure_lookback_runs: usize,
1017) -> Result<Vec<DsaCalibrationRow>> {
1018    grid.validate()?;
1019
1020    let mut rows = Vec::with_capacity(grid.grid_point_count());
1021    for (config_id, config) in grid.expand().into_iter().enumerate() {
1022        let evaluation = evaluate_dsa(
1023            dataset,
1024            nominal,
1025            residuals,
1026            signs,
1027            baselines,
1028            grammar,
1029            &config,
1030            pre_failure_lookback_runs,
1031        )?;
1032        rows.push(DsaCalibrationRow {
1033            config_id,
1034            primary_run_signal: evaluation.run_signals.primary_run_signal.clone(),
1035            window: config.window,
1036            persistence_runs: config.persistence_runs,
1037            alert_tau: config.alert_tau,
1038            corroborating_feature_count_min: config.corroborating_feature_count_min,
1039            failure_run_recall: evaluation.summary.failure_run_recall,
1040            failure_runs: evaluation.summary.failure_runs,
1041            threshold_failure_run_recall: evaluation
1042                .comparison_summary
1043                .threshold
1044                .failure_run_recall,
1045            ewma_failure_run_recall: evaluation.comparison_summary.ewma.failure_run_recall,
1046            failure_recall_delta_vs_threshold: evaluation
1047                .comparison_summary
1048                .failure_recall_delta_vs_threshold,
1049            failure_recall_delta_vs_ewma: evaluation
1050                .comparison_summary
1051                .failure_recall_delta_vs_ewma,
1052            mean_lead_time_runs: evaluation.summary.mean_lead_time_runs,
1053            median_lead_time_runs: evaluation.summary.median_lead_time_runs,
1054            pass_run_nuisance_proxy: evaluation.summary.pass_run_nuisance_proxy,
1055            mean_lead_delta_vs_cusum_runs: evaluation.summary.mean_lead_delta_vs_cusum_runs,
1056            mean_lead_delta_vs_run_energy_runs: evaluation
1057                .summary
1058                .mean_lead_delta_vs_run_energy_runs,
1059            mean_lead_delta_vs_pca_fdc_runs: evaluation.summary.mean_lead_delta_vs_pca_fdc_runs,
1060            mean_lead_delta_vs_threshold_runs: evaluation.summary.mean_lead_delta_vs_threshold_runs,
1061            mean_lead_delta_vs_ewma_runs: evaluation.summary.mean_lead_delta_vs_ewma_runs,
1062            pass_run_nuisance_delta_vs_cusum: evaluation
1063                .comparison_summary
1064                .pass_run_nuisance_delta_vs_cusum,
1065            pass_run_nuisance_delta_vs_run_energy: evaluation
1066                .comparison_summary
1067                .pass_run_nuisance_delta_vs_run_energy,
1068            pass_run_nuisance_delta_vs_pca_fdc: evaluation
1069                .comparison_summary
1070                .pass_run_nuisance_delta_vs_pca_fdc,
1071            pass_run_nuisance_delta_vs_threshold: evaluation
1072                .comparison_summary
1073                .pass_run_nuisance_delta_vs_threshold,
1074            pass_run_nuisance_delta_vs_ewma: evaluation
1075                .comparison_summary
1076                .pass_run_nuisance_delta_vs_ewma,
1077            pass_run_nuisance_delta_vs_raw_boundary: evaluation
1078                .comparison_summary
1079                .pass_run_nuisance_delta_vs_raw_boundary,
1080            raw_boundary_episode_count: evaluation.episode_summary.raw_boundary_episode_count,
1081            dsa_episode_count: evaluation.episode_summary.dsa_episode_count,
1082            dsa_episodes_preceding_failure: evaluation
1083                .episode_summary
1084                .dsa_episodes_preceding_failure,
1085            mean_dsa_episode_length_runs: evaluation.episode_summary.mean_dsa_episode_length_runs,
1086            max_dsa_episode_length_runs: evaluation.episode_summary.max_dsa_episode_length_runs,
1087            compression_ratio: evaluation.episode_summary.compression_ratio,
1088            precursor_quality: evaluation.episode_summary.precursor_quality,
1089            non_escalating_dsa_episode_fraction: evaluation
1090                .episode_summary
1091                .non_escalating_dsa_episode_fraction,
1092            nuisance_improved: evaluation.comparison_summary.nuisance_improved,
1093            lead_time_improved: evaluation.comparison_summary.lead_time_improved,
1094            recall_preserved: evaluation.comparison_summary.recall_preserved,
1095            compression_improved: evaluation.comparison_summary.compression_improved,
1096            any_metric_improved: evaluation.summary.any_metric_improved,
1097            nothing_improved: evaluation.comparison_summary.nothing_improved,
1098            threshold_recall_gate_passed: evaluation.summary.threshold_recall_gate_passed,
1099            boundary_nuisance_gate_passed: evaluation.summary.boundary_nuisance_gate_passed,
1100            primary_success_condition_met: evaluation.summary.primary_success_condition_met,
1101            validation_passed: evaluation.summary.validation_passed,
1102            success_condition_failures: evaluation.summary.success_condition_failures.join("; "),
1103            validation_failures: evaluation.summary.validation_failures.join("; "),
1104        });
1105    }
1106
1107    Ok(rows)
1108}
1109
1110fn empty_trace(feature_index: usize, feature_name: &str, run_count: usize) -> DsaFeatureTrace {
1111    DsaFeatureTrace {
1112        feature_index,
1113        feature_name: feature_name.into(),
1114        boundary_basis_hit: vec![false; run_count],
1115        drift_outward_hit: vec![false; run_count],
1116        slew_hit: vec![false; run_count],
1117        motif_hit: vec![false; run_count],
1118        boundary_density_w: vec![0.0; run_count],
1119        drift_persistence_w: vec![0.0; run_count],
1120        slew_density_w: vec![0.0; run_count],
1121        ewma_occupancy_w: vec![0.0; run_count],
1122        motif_recurrence_w: vec![0.0; run_count],
1123        fragmentation_proxy_w: vec![0.0; run_count],
1124        consistent: vec![true; run_count],
1125        dsa_score: vec![0.0; run_count],
1126        dsa_active: vec![false; run_count],
1127        numeric_dsa_alert: vec![false; run_count],
1128        dsa_alert: vec![false; run_count],
1129        resolved_alert_class: vec![HeuristicAlertClass::Silent; run_count],
1130        policy_state: vec![DsaPolicyState::Silent; run_count],
1131        policy_suppressed_to_silent: vec![false; run_count],
1132        rescue_transition: vec!["none".into(); run_count],
1133        rescued_to_review: vec![false; run_count],
1134        motif_policy_contributions: Vec::new(),
1135    }
1136}
1137
1138fn assemble_dsa_evaluation(
1139    dataset: &PreparedDataset,
1140    nominal: &NominalModel,
1141    residuals: &ResidualSet,
1142    baselines: &BaselineSet,
1143    grammar: &GrammarSet,
1144    traces: Vec<DsaFeatureTrace>,
1145    config: &DsaConfig,
1146    weights: &DsaWeights,
1147    pre_failure_lookback_runs: usize,
1148    policy_runtime: &DsaPolicyRuntime,
1149    raw_boundary_episode_feature_mask: Option<&[bool]>,
1150    analyzable_feature_count_override: Option<usize>,
1151) -> DsaEvaluation {
1152    let run_count = dataset.labels.len();
1153    let motif_names = dsa_contributing_motif_names()
1154        .iter()
1155        .map(|name| (*name).to_string())
1156        .collect::<Vec<_>>();
1157    let raw_boundary_run_signal = (0..run_count)
1158        .map(|run_index| {
1159            grammar
1160                .traces
1161                .iter()
1162                .any(|trace| trace.raw_states[run_index] == GrammarState::Boundary)
1163        })
1164        .collect::<Vec<_>>();
1165    let raw_violation_run_signal = (0..run_count)
1166        .map(|run_index| {
1167            grammar
1168                .traces
1169                .iter()
1170                .any(|trace| trace.raw_states[run_index] == GrammarState::Violation)
1171        })
1172        .collect::<Vec<_>>();
1173    let threshold_run_signal = (0..run_count)
1174        .map(|run_index| {
1175            residuals
1176                .traces
1177                .iter()
1178                .any(|trace| trace.threshold_alarm[run_index])
1179        })
1180        .collect::<Vec<_>>();
1181    let ewma_run_signal = (0..run_count)
1182        .map(|run_index| baselines.ewma.iter().any(|trace| trace.alarm[run_index]))
1183        .collect::<Vec<_>>();
1184    let cusum_run_signal = (0..run_count)
1185        .map(|run_index| baselines.cusum.iter().any(|trace| trace.alarm[run_index]))
1186        .collect::<Vec<_>>();
1187    let run_energy_run_signal = baselines.run_energy.alarm.clone();
1188    let pca_fdc_run_signal = baselines.pca_fdc.alarm.clone();
1189    let run_signals = build_run_signals(&traces, &raw_violation_run_signal, config, run_count);
1190
1191    let failure_indices = dataset
1192        .labels
1193        .iter()
1194        .enumerate()
1195        .filter_map(|(index, label)| (*label == 1).then_some(index))
1196        .collect::<Vec<_>>();
1197    let pass_indices = dataset
1198        .labels
1199        .iter()
1200        .enumerate()
1201        .filter_map(|(index, label)| (*label == -1).then_some(index))
1202        .collect::<Vec<_>>();
1203    let failure_window_mask =
1204        build_failure_window_mask(run_count, &failure_indices, pre_failure_lookback_runs);
1205
1206    let raw_boundary_leads = failure_indices
1207        .iter()
1208        .map(|&failure_index| {
1209            let window_start = failure_index.saturating_sub(pre_failure_lookback_runs);
1210            earliest_run_signal(&raw_boundary_run_signal, window_start, failure_index)
1211                .map(|run_index| failure_index - run_index)
1212        })
1213        .collect::<Vec<_>>();
1214    let raw_violation_leads = failure_indices
1215        .iter()
1216        .map(|&failure_index| {
1217            let window_start = failure_index.saturating_sub(pre_failure_lookback_runs);
1218            earliest_run_signal(&raw_violation_run_signal, window_start, failure_index)
1219                .map(|run_index| failure_index - run_index)
1220        })
1221        .collect::<Vec<_>>();
1222    let threshold_leads = failure_indices
1223        .iter()
1224        .map(|&failure_index| {
1225            let window_start = failure_index.saturating_sub(pre_failure_lookback_runs);
1226            earliest_run_signal(&threshold_run_signal, window_start, failure_index)
1227                .map(|run_index| failure_index - run_index)
1228        })
1229        .collect::<Vec<_>>();
1230    let ewma_leads = failure_indices
1231        .iter()
1232        .map(|&failure_index| {
1233            let window_start = failure_index.saturating_sub(pre_failure_lookback_runs);
1234            earliest_run_signal(&ewma_run_signal, window_start, failure_index)
1235                .map(|run_index| failure_index - run_index)
1236        })
1237        .collect::<Vec<_>>();
1238    let cusum_leads = failure_indices
1239        .iter()
1240        .map(|&failure_index| {
1241            let window_start = failure_index.saturating_sub(pre_failure_lookback_runs);
1242            earliest_run_signal(&cusum_run_signal, window_start, failure_index)
1243                .map(|run_index| failure_index - run_index)
1244        })
1245        .collect::<Vec<_>>();
1246    let run_energy_leads = failure_indices
1247        .iter()
1248        .map(|&failure_index| {
1249            let window_start = failure_index.saturating_sub(pre_failure_lookback_runs);
1250            earliest_run_signal(&run_energy_run_signal, window_start, failure_index)
1251                .map(|run_index| failure_index - run_index)
1252        })
1253        .collect::<Vec<_>>();
1254    let pca_fdc_leads = failure_indices
1255        .iter()
1256        .map(|&failure_index| {
1257            let window_start = failure_index.saturating_sub(pre_failure_lookback_runs);
1258            earliest_run_signal(&pca_fdc_run_signal, window_start, failure_index)
1259                .map(|run_index| failure_index - run_index)
1260        })
1261        .collect::<Vec<_>>();
1262    let numeric_dsa_leads = failure_indices
1263        .iter()
1264        .map(|&failure_index| {
1265            let window_start = failure_index.saturating_sub(pre_failure_lookback_runs);
1266            earliest_run_signal(
1267                &run_signals.numeric_primary_run_alert,
1268                window_start,
1269                failure_index,
1270            )
1271            .map(|run_index| failure_index - run_index)
1272        })
1273        .collect::<Vec<_>>();
1274
1275    let per_failure_run_signals = failure_indices
1276        .iter()
1277        .map(|&failure_index| {
1278            let window_start = failure_index.saturating_sub(pre_failure_lookback_runs);
1279            let earliest_dsa = earliest_primary_signal(
1280                &traces,
1281                &run_signals.primary_run_alert,
1282                window_start,
1283                failure_index,
1284            );
1285            let earliest_threshold_run =
1286                earliest_run_signal(&threshold_run_signal, window_start, failure_index);
1287            let earliest_ewma_run =
1288                earliest_run_signal(&ewma_run_signal, window_start, failure_index);
1289            let earliest_cusum_run =
1290                earliest_run_signal(&cusum_run_signal, window_start, failure_index);
1291            let earliest_run_energy_run =
1292                earliest_run_signal(&run_energy_run_signal, window_start, failure_index);
1293            let earliest_pca_fdc_run =
1294                earliest_run_signal(&pca_fdc_run_signal, window_start, failure_index);
1295            let dsa_lead_runs = earliest_dsa
1296                .as_ref()
1297                .map(|signal| failure_index - signal.run_index);
1298            let threshold_lead_runs = earliest_threshold_run.map(|index| failure_index - index);
1299            let ewma_lead_runs = earliest_ewma_run.map(|index| failure_index - index);
1300            let cusum_lead_runs = earliest_cusum_run.map(|index| failure_index - index);
1301            let run_energy_lead_runs = earliest_run_energy_run.map(|index| failure_index - index);
1302            let pca_fdc_lead_runs = earliest_pca_fdc_run.map(|index| failure_index - index);
1303            let alerting_feature_count = traces
1304                .iter()
1305                .filter(|trace| {
1306                    trace.dsa_alert[window_start..failure_index]
1307                        .iter()
1308                        .any(|flag| *flag)
1309                })
1310                .count();
1311            let max_score = max_dsa_score(&traces, window_start, failure_index);
1312
1313            PerFailureRunDsaSignal {
1314                failure_run_index: failure_index,
1315                failure_timestamp: dataset.timestamps[failure_index]
1316                    .format("%Y-%m-%d %H:%M:%S")
1317                    .to_string(),
1318                earliest_dsa_run: earliest_dsa.as_ref().map(|signal| signal.run_index),
1319                earliest_primary_source: earliest_dsa.as_ref().map(|signal| signal.source.clone()),
1320                earliest_dsa_feature_index: earliest_dsa
1321                    .as_ref()
1322                    .map(|signal| signal.feature_index),
1323                earliest_dsa_feature_name: earliest_dsa
1324                    .as_ref()
1325                    .map(|signal| signal.feature_name.clone()),
1326                dsa_lead_runs,
1327                threshold_lead_runs,
1328                ewma_lead_runs,
1329                cusum_lead_runs,
1330                run_energy_lead_runs,
1331                pca_fdc_lead_runs,
1332                dsa_minus_cusum_delta_runs: paired_delta(dsa_lead_runs, cusum_lead_runs),
1333                dsa_minus_run_energy_delta_runs: paired_delta(dsa_lead_runs, run_energy_lead_runs),
1334                dsa_minus_pca_fdc_delta_runs: paired_delta(dsa_lead_runs, pca_fdc_lead_runs),
1335                dsa_minus_threshold_delta_runs: paired_delta(dsa_lead_runs, threshold_lead_runs),
1336                dsa_minus_ewma_delta_runs: paired_delta(dsa_lead_runs, ewma_lead_runs),
1337                dsa_alerting_feature_count: alerting_feature_count,
1338                max_dsa_score_in_lookback: max_score.as_ref().map(|score| score.score),
1339                max_dsa_score_feature_index: max_score.as_ref().map(|score| score.feature_index),
1340                max_dsa_score_feature_name: max_score
1341                    .as_ref()
1342                    .map(|score| score.feature_name.clone()),
1343                max_dsa_score_run_index: max_score.as_ref().map(|score| score.run_index),
1344                max_dsa_score_boundary_density_w: max_score
1345                    .as_ref()
1346                    .map(|score| score.boundary_density_w),
1347                max_dsa_score_drift_persistence_w: max_score
1348                    .as_ref()
1349                    .map(|score| score.drift_persistence_w),
1350                max_dsa_score_slew_density_w: max_score.as_ref().map(|score| score.slew_density_w),
1351                max_dsa_score_ewma_occupancy_w: max_score
1352                    .as_ref()
1353                    .map(|score| score.ewma_occupancy_w),
1354                max_dsa_score_motif_recurrence_w: max_score
1355                    .as_ref()
1356                    .map(|score| score.motif_recurrence_w),
1357                max_dsa_score_fragmentation_proxy_w: max_score
1358                    .as_ref()
1359                    .map(|score| score.fragmentation_proxy_w),
1360                max_dsa_score_consistent: max_score.as_ref().map(|score| score.consistent),
1361                max_dsa_score_policy_state: max_score
1362                    .as_ref()
1363                    .map(|score| score.policy_state.as_lowercase().to_string()),
1364                max_dsa_score_resolved_alert_class: max_score
1365                    .as_ref()
1366                    .map(|score| format!("{:?}", score.resolved_alert_class)),
1367                max_dsa_score_numeric_dsa_alert: max_score
1368                    .as_ref()
1369                    .map(|score| score.numeric_dsa_alert),
1370                max_dsa_score_dsa_alert: max_score.as_ref().map(|score| score.dsa_alert),
1371                max_dsa_score_policy_suppressed: max_score
1372                    .as_ref()
1373                    .map(|score| score.policy_suppressed_to_silent),
1374                max_dsa_score_rescue_transition: max_score.as_ref().and_then(|score| {
1375                    (score.rescue_transition != "none").then_some(score.rescue_transition.clone())
1376                }),
1377            }
1378        })
1379        .collect::<Vec<_>>();
1380
1381    let alert_point_count = traces
1382        .iter()
1383        .map(|trace| trace.dsa_alert.iter().filter(|flag| **flag).count())
1384        .sum::<usize>();
1385    let alert_run_count = run_signals
1386        .primary_run_alert
1387        .iter()
1388        .filter(|flag| **flag)
1389        .count();
1390    let numeric_alert_point_count = traces
1391        .iter()
1392        .map(|trace| trace.numeric_dsa_alert.iter().filter(|flag| **flag).count())
1393        .sum::<usize>();
1394    let numeric_alert_run_count = run_signals
1395        .numeric_primary_run_alert
1396        .iter()
1397        .filter(|flag| **flag)
1398        .count();
1399    let watch_point_count = traces
1400        .iter()
1401        .map(|trace| {
1402            trace
1403                .policy_state
1404                .iter()
1405                .filter(|state| **state == DsaPolicyState::Watch)
1406                .count()
1407        })
1408        .sum::<usize>();
1409    let review_point_count = traces
1410        .iter()
1411        .map(|trace| {
1412            trace
1413                .policy_state
1414                .iter()
1415                .filter(|state| **state == DsaPolicyState::Review)
1416                .count()
1417        })
1418        .sum::<usize>();
1419    let escalate_point_count = traces
1420        .iter()
1421        .map(|trace| {
1422            trace
1423                .policy_state
1424                .iter()
1425                .filter(|state| **state == DsaPolicyState::Escalate)
1426                .count()
1427        })
1428        .sum::<usize>();
1429    let silenced_point_count = traces
1430        .iter()
1431        .map(|trace| {
1432            trace
1433                .policy_suppressed_to_silent
1434                .iter()
1435                .filter(|flag| **flag)
1436                .count()
1437        })
1438        .sum::<usize>();
1439    let rescued_point_count = traces
1440        .iter()
1441        .map(|trace| {
1442            trace
1443                .rescue_transition
1444                .iter()
1445                .filter(|transition| transition.as_str() != "none")
1446                .count()
1447        })
1448        .sum::<usize>();
1449    let rescued_watch_to_review_points = traces
1450        .iter()
1451        .map(|trace| {
1452            trace
1453                .rescue_transition
1454                .iter()
1455                .filter(|transition| transition.as_str() == "watch_to_review")
1456                .count()
1457        })
1458        .sum::<usize>();
1459    let rescued_review_to_escalate_points = traces
1460        .iter()
1461        .map(|trace| {
1462            trace
1463                .rescue_transition
1464                .iter()
1465                .filter(|transition| transition.as_str() == "review_to_escalate")
1466                .count()
1467        })
1468        .sum::<usize>();
1469    let failure_run_recall = per_failure_run_signals
1470        .iter()
1471        .filter(|signal| signal.earliest_dsa_run.is_some())
1472        .count();
1473    let dsa_row = SignalComparisonRow {
1474        signal: "DSA".into(),
1475        failure_run_recall,
1476        failure_runs: failure_indices.len(),
1477        failure_run_recall_rate: rate(failure_run_recall, failure_indices.len()),
1478        mean_lead_time_runs: mean_option_usize(
1479            &per_failure_run_signals
1480                .iter()
1481                .map(|signal| signal.dsa_lead_runs)
1482                .collect::<Vec<_>>(),
1483        ),
1484        median_lead_time_runs: median_option_usize(
1485            &per_failure_run_signals
1486                .iter()
1487                .map(|signal| signal.dsa_lead_runs)
1488                .collect::<Vec<_>>(),
1489        ),
1490        pass_run_nuisance_proxy: rate(
1491            pass_indices
1492                .iter()
1493                .filter(|&&run_index| run_signals.primary_run_alert[run_index])
1494                .count(),
1495            pass_indices.len(),
1496        ),
1497        mean_lead_delta_vs_cusum_runs: mean_option_i64(
1498            &per_failure_run_signals
1499                .iter()
1500                .map(|signal| signal.dsa_minus_cusum_delta_runs)
1501                .collect::<Vec<_>>(),
1502        ),
1503        mean_lead_delta_vs_run_energy_runs: mean_option_i64(
1504            &per_failure_run_signals
1505                .iter()
1506                .map(|signal| signal.dsa_minus_run_energy_delta_runs)
1507                .collect::<Vec<_>>(),
1508        ),
1509        mean_lead_delta_vs_pca_fdc_runs: mean_option_i64(
1510            &per_failure_run_signals
1511                .iter()
1512                .map(|signal| signal.dsa_minus_pca_fdc_delta_runs)
1513                .collect::<Vec<_>>(),
1514        ),
1515        mean_lead_delta_vs_threshold_runs: mean_option_i64(
1516            &per_failure_run_signals
1517                .iter()
1518                .map(|signal| signal.dsa_minus_threshold_delta_runs)
1519                .collect::<Vec<_>>(),
1520        ),
1521        mean_lead_delta_vs_ewma_runs: mean_option_i64(
1522            &per_failure_run_signals
1523                .iter()
1524                .map(|signal| signal.dsa_minus_ewma_delta_runs)
1525                .collect::<Vec<_>>(),
1526        ),
1527    };
1528    let numeric_dsa_row = SignalComparisonRow {
1529        signal: "Numeric-only DSA".into(),
1530        failure_run_recall: count_present(numeric_dsa_leads.iter().copied()),
1531        failure_runs: failure_indices.len(),
1532        failure_run_recall_rate: rate(
1533            count_present(numeric_dsa_leads.iter().copied()),
1534            failure_indices.len(),
1535        ),
1536        mean_lead_time_runs: mean_option_usize(&numeric_dsa_leads),
1537        median_lead_time_runs: median_option_usize(&numeric_dsa_leads),
1538        pass_run_nuisance_proxy: rate(
1539            pass_indices
1540                .iter()
1541                .filter(|&&run_index| run_signals.numeric_primary_run_alert[run_index])
1542                .count(),
1543            pass_indices.len(),
1544        ),
1545        mean_lead_delta_vs_cusum_runs: None,
1546        mean_lead_delta_vs_run_energy_runs: None,
1547        mean_lead_delta_vs_pca_fdc_runs: None,
1548        mean_lead_delta_vs_threshold_runs: None,
1549        mean_lead_delta_vs_ewma_runs: None,
1550    };
1551    let motif_policy_contributions = aggregate_motif_policy_contributions(&traces);
1552    let threshold_row = baseline_row(
1553        "Threshold",
1554        failure_indices.len(),
1555        &threshold_leads,
1556        rate(
1557            pass_indices
1558                .iter()
1559                .filter(|&&run_index| threshold_run_signal[run_index])
1560                .count(),
1561            pass_indices.len(),
1562        ),
1563    );
1564    let ewma_row = baseline_row(
1565        "EWMA",
1566        failure_indices.len(),
1567        &ewma_leads,
1568        rate(
1569            pass_indices
1570                .iter()
1571                .filter(|&&run_index| ewma_run_signal[run_index])
1572                .count(),
1573            pass_indices.len(),
1574        ),
1575    );
1576    let cusum_row = baseline_row(
1577        "CUSUM",
1578        failure_indices.len(),
1579        &cusum_leads,
1580        rate(
1581            pass_indices
1582                .iter()
1583                .filter(|&&run_index| cusum_run_signal[run_index])
1584                .count(),
1585            pass_indices.len(),
1586        ),
1587    );
1588    let run_energy_row = baseline_row(
1589        "Run energy",
1590        failure_indices.len(),
1591        &run_energy_leads,
1592        rate(
1593            pass_indices
1594                .iter()
1595                .filter(|&&run_index| run_energy_run_signal[run_index])
1596                .count(),
1597            pass_indices.len(),
1598        ),
1599    );
1600    let pca_fdc_row = baseline_row(
1601        "PCA T2/SPE",
1602        failure_indices.len(),
1603        &pca_fdc_leads,
1604        rate(
1605            pass_indices
1606                .iter()
1607                .filter(|&&run_index| pca_fdc_run_signal[run_index])
1608                .count(),
1609            pass_indices.len(),
1610        ),
1611    );
1612    let dsfb_violation_row = baseline_row(
1613        "DSFB Violation",
1614        failure_indices.len(),
1615        &raw_violation_leads,
1616        rate(
1617            pass_indices
1618                .iter()
1619                .filter(|&&run_index| raw_violation_run_signal[run_index])
1620                .count(),
1621            pass_indices.len(),
1622        ),
1623    );
1624    let dsfb_raw_boundary_row = baseline_row(
1625        "DSFB Raw Boundary",
1626        failure_indices.len(),
1627        &raw_boundary_leads,
1628        rate(
1629            pass_indices
1630                .iter()
1631                .filter(|&&run_index| raw_boundary_run_signal[run_index])
1632                .count(),
1633            pass_indices.len(),
1634        ),
1635    );
1636
1637    let raw_boundary_episode_count = grammar
1638        .traces
1639        .iter()
1640        .enumerate()
1641        .filter(|(feature_index, _)| {
1642            raw_boundary_episode_feature_mask
1643                .map(|mask| mask.get(*feature_index).copied().unwrap_or(false))
1644                .unwrap_or(true)
1645        })
1646        .map(|(_, trace)| {
1647            episode_ranges(
1648                &trace
1649                    .raw_states
1650                    .iter()
1651                    .map(|state| *state == GrammarState::Boundary)
1652                    .collect::<Vec<_>>(),
1653            )
1654            .len()
1655        })
1656        .sum::<usize>();
1657    let episode_summary = compute_episode_summary(
1658        &run_signals.primary_run_signal,
1659        &run_signals.primary_run_alert,
1660        raw_boundary_episode_count,
1661        &raw_violation_run_signal,
1662        &failure_window_mask,
1663    );
1664    let component_contributions = component_contributions(&traces);
1665    let nuisance_improved = dsa_row.pass_run_nuisance_proxy < threshold_row.pass_run_nuisance_proxy
1666        || dsa_row.pass_run_nuisance_proxy < ewma_row.pass_run_nuisance_proxy
1667        || dsa_row.pass_run_nuisance_proxy < cusum_row.pass_run_nuisance_proxy
1668        || dsa_row.pass_run_nuisance_proxy < run_energy_row.pass_run_nuisance_proxy
1669        || dsa_row.pass_run_nuisance_proxy < pca_fdc_row.pass_run_nuisance_proxy
1670        || dsa_row.pass_run_nuisance_proxy < dsfb_raw_boundary_row.pass_run_nuisance_proxy;
1671    let lead_time_improved = matches!(dsa_row.mean_lead_delta_vs_threshold_runs, Some(delta) if delta > 0.0)
1672        || matches!(dsa_row.mean_lead_delta_vs_ewma_runs, Some(delta) if delta > 0.0);
1673    let recall_preserved = dsa_row.failure_run_recall >= threshold_row.failure_run_recall
1674        && dsa_row.failure_run_recall >= ewma_row.failure_run_recall
1675        && dsa_row.failure_run_recall >= dsfb_violation_row.failure_run_recall;
1676    let recall_improved = dsa_row.failure_run_recall > threshold_row.failure_run_recall
1677        || dsa_row.failure_run_recall > ewma_row.failure_run_recall
1678        || dsa_row.failure_run_recall > dsfb_violation_row.failure_run_recall;
1679    let compression_improved =
1680        matches!(episode_summary.compression_ratio, Some(ratio) if ratio > 1.0);
1681    let any_metric_improved =
1682        nuisance_improved || lead_time_improved || recall_improved || compression_improved;
1683    let nothing_improved = !any_metric_improved;
1684    let threshold_recall_gate_passed =
1685        dsa_row.failure_run_recall >= threshold_row.failure_run_recall;
1686    let boundary_nuisance_gate_passed =
1687        dsa_row.pass_run_nuisance_proxy < dsfb_raw_boundary_row.pass_run_nuisance_proxy;
1688    let success_condition_failures =
1689        success_condition_failures(&dsa_row, &threshold_row, &ewma_row);
1690    let primary_success_condition_met = success_condition_failures.is_empty();
1691    let validation_failures = validation_failures(
1692        &dsa_row,
1693        &threshold_row,
1694        dsfb_raw_boundary_row.pass_run_nuisance_proxy,
1695        threshold_recall_gate_passed,
1696        boundary_nuisance_gate_passed,
1697        any_metric_improved,
1698    );
1699    let validation_passed = validation_failures.is_empty();
1700    let conclusion = dsa_conclusion(
1701        &dsa_row,
1702        &threshold_row,
1703        &ewma_row,
1704        &dsfb_violation_row,
1705        &dsfb_raw_boundary_row,
1706        &episode_summary,
1707        primary_success_condition_met,
1708        nuisance_improved,
1709        lead_time_improved,
1710        recall_preserved,
1711        compression_improved,
1712        nothing_improved,
1713        &component_contributions,
1714        &success_condition_failures,
1715        &validation_failures,
1716    );
1717
1718    let comparison_summary = DsaVsBaselinesSummary {
1719        dataset: "SECOM".into(),
1720        primary_run_signal: run_signals.primary_run_signal.clone(),
1721        dsa: dsa_row.clone(),
1722        numeric_dsa: numeric_dsa_row.clone(),
1723        threshold: threshold_row.clone(),
1724        ewma: ewma_row.clone(),
1725        cusum: cusum_row.clone(),
1726        run_energy: run_energy_row.clone(),
1727        pca_fdc: pca_fdc_row.clone(),
1728        dsfb_violation: dsfb_violation_row.clone(),
1729        dsfb_raw_boundary: dsfb_raw_boundary_row.clone(),
1730        episode_summary: episode_summary.clone(),
1731        failure_recall_delta_vs_threshold: dsa_row.failure_run_recall as i64
1732            - threshold_row.failure_run_recall as i64,
1733        failure_recall_delta_vs_ewma: dsa_row.failure_run_recall as i64
1734            - ewma_row.failure_run_recall as i64,
1735        failure_recall_delta_vs_cusum: dsa_row.failure_run_recall as i64
1736            - cusum_row.failure_run_recall as i64,
1737        failure_recall_delta_vs_run_energy: dsa_row.failure_run_recall as i64
1738            - run_energy_row.failure_run_recall as i64,
1739        failure_recall_delta_vs_pca_fdc: dsa_row.failure_run_recall as i64
1740            - pca_fdc_row.failure_run_recall as i64,
1741        failure_recall_delta_vs_violation: dsa_row.failure_run_recall as i64
1742            - dsfb_violation_row.failure_run_recall as i64,
1743        pass_run_nuisance_delta_vs_threshold: dsa_row.pass_run_nuisance_proxy
1744            - threshold_row.pass_run_nuisance_proxy,
1745        pass_run_nuisance_delta_vs_ewma: dsa_row.pass_run_nuisance_proxy
1746            - ewma_row.pass_run_nuisance_proxy,
1747        pass_run_nuisance_delta_vs_violation: dsa_row.pass_run_nuisance_proxy
1748            - dsfb_violation_row.pass_run_nuisance_proxy,
1749        pass_run_nuisance_delta_vs_cusum: dsa_row.pass_run_nuisance_proxy
1750            - cusum_row.pass_run_nuisance_proxy,
1751        pass_run_nuisance_delta_vs_run_energy: dsa_row.pass_run_nuisance_proxy
1752            - run_energy_row.pass_run_nuisance_proxy,
1753        pass_run_nuisance_delta_vs_pca_fdc: dsa_row.pass_run_nuisance_proxy
1754            - pca_fdc_row.pass_run_nuisance_proxy,
1755        pass_run_nuisance_delta_vs_raw_boundary: dsa_row.pass_run_nuisance_proxy
1756            - dsfb_raw_boundary_row.pass_run_nuisance_proxy,
1757        pass_run_nuisance_delta_vs_numeric_dsa: dsa_row.pass_run_nuisance_proxy
1758            - numeric_dsa_row.pass_run_nuisance_proxy,
1759        precursor_quality: episode_summary.precursor_quality,
1760        dsa_episodes_preceding_failure: episode_summary.dsa_episodes_preceding_failure,
1761        component_contributions: component_contributions.clone(),
1762        motif_policy_contributions: motif_policy_contributions.clone(),
1763        policy_vs_numeric_recall_delta: dsa_row.failure_run_recall as i64
1764            - numeric_dsa_row.failure_run_recall as i64,
1765        watch_point_count,
1766        review_point_count,
1767        escalate_point_count,
1768        silenced_point_count,
1769        nuisance_improved,
1770        lead_time_improved,
1771        recall_preserved,
1772        compression_improved,
1773        nothing_improved,
1774        threshold_recall_gate_passed,
1775        boundary_nuisance_gate_passed,
1776        primary_success_condition_met,
1777        any_metric_improved,
1778        validation_passed,
1779        success_condition_failures: success_condition_failures.clone(),
1780        validation_failures: validation_failures.clone(),
1781        conclusion,
1782    };
1783    let parameter_manifest = DsaParameterManifest {
1784        config: config.clone(),
1785        weights: weights.clone(),
1786        feature_policy_override_count: policy_runtime.feature_policy_overrides.len(),
1787        feature_policy_override_summary: policy_runtime
1788            .feature_policy_overrides
1789            .iter()
1790            .map(|override_entry| {
1791                format!(
1792                    "{}: rescue_eligible={}, rescue_priority={}, allow_watch_only={}, allow_review_without_escalate={}, suppress_if_isolated={}, override_reason={}",
1793                    override_entry.feature_name,
1794                    override_entry.rescue_eligible,
1795                    override_entry.rescue_priority,
1796                    override_entry
1797                        .allow_watch_only
1798                        .map(|value| value.to_string())
1799                        .unwrap_or_else(|| "default".into()),
1800                    override_entry
1801                        .allow_review_without_escalate
1802                        .map(|value| value.to_string())
1803                        .unwrap_or_else(|| "default".into()),
1804                    override_entry
1805                        .suppress_if_isolated
1806                        .map(|value| value.to_string())
1807                        .unwrap_or_else(|| "default".into()),
1808                    override_entry.override_reason
1809                )
1810            })
1811            .collect(),
1812        policy_engine_definition:
1813            "Deterministic heuristics-governed policy engine over structural DSA candidates with explicit Silent/Watch/Review/Escalate feature states."
1814                .into(),
1815        feature_level_state_definition:
1816            "Feature state is resolved from active motif policies, structural score >= tau, persistence gating, directional consistency, local corroboration, and explicit silence suppression."
1817                .into(),
1818        primary_run_signal: run_signals.primary_run_signal.clone(),
1819        primary_run_signal_definition: format!(
1820            "Primary run-level DSA decision is feature_count_review_or_escalate(k) >= {}. This is a cross-feature corroboration gate above the frozen DSFB states and scalar baselines.",
1821            config.corroborating_feature_count_min
1822        ),
1823        secondary_run_signal: "any_feature_review_or_escalate(k)".into(),
1824        tertiary_run_signal: "feature_count_escalate(k)".into(),
1825        strict_escalate_signal: format!(
1826            "feature_count_escalate(k) >= {}",
1827            config.corroborating_feature_count_min
1828        ),
1829        rolling_window_definition: format!(
1830            "Trailing inclusive rolling window of up to {} runs per analyzable feature.",
1831            config.window
1832        ),
1833        boundary_density_basis:
1834            "Fraction of the trailing window where the feature is in the raw DSFB Boundary state."
1835                .into(),
1836        drift_persistence_definition:
1837            "Fraction of the trailing window where drift >= drift threshold and the drift sign is outward (positive residual-norm drift)."
1838                .into(),
1839        slew_density_definition:
1840            "Fraction of the trailing window where absolute slew >= slew threshold.".into(),
1841        ewma_occupancy_formula:
1842            "Mean over the trailing window of clamp(EWMA / EWMA_threshold, 0, 1).".into(),
1843        motif_names_used_for_recurrence: motif_names,
1844        directional_consistency_rule:
1845            "CONSISTENT is true only when thresholded drift signs in the window are never inward and the nonzero drift sign never flips within the window."
1846                .into(),
1847        silence_rule:
1848            "A structural candidate remains Silent when no motif policy activates, when motif hits stay below deterministic minimum_hits/minimum_window rules, when fragmentation exceeds the motif ceiling, or when persistence/corroboration gates fail."
1849                .into(),
1850        corroboration_rule: format!(
1851            "RUN_LEVEL_DSA(k) is true only when the count of features in Review or Escalate is at least {}.",
1852            config.corroborating_feature_count_min
1853        ),
1854        recall_rescue_definition: if policy_runtime.recall_rescue.enabled {
1855            format!(
1856                "Bounded recall rescue is enabled for explicit feature overrides only. Rescue promotes grammar-qualified near-miss structure when dsa_score >= tau - margin, boundary_density >= {:.2}, motif_recurrence >= {:.2}, ewma_occupancy >= {:.2}, and the override-specific watch-hit / fragmentation guards pass. Priority-2 overrides may rescue repeated Watch-class structure even when the directional-consistency rule fails, and priority-3 overrides may recover explicit grammar-qualified semantic support features from Silent when a failure-local override is present.",
1857                policy_runtime.recall_rescue.minimum_boundary_density,
1858                policy_runtime.recall_rescue.minimum_motif_recurrence,
1859                policy_runtime.recall_rescue.minimum_ewma_occupancy,
1860            )
1861        } else {
1862            "Bounded recall rescue is disabled.".into()
1863        },
1864        recall_tolerance_runs_for_primary_success: DSA_PRIMARY_SUCCESS_RECALL_TOLERANCE_RUNS,
1865        primary_success_condition_definition: dsa_primary_success_condition_definition(),
1866        optimization_priority_order: dsa_optimization_priority_order(),
1867    };
1868
1869    DsaEvaluation {
1870        traces,
1871        run_signals: run_signals.clone(),
1872        episode_summary: episode_summary.clone(),
1873        parameter_manifest,
1874        policy_runtime: policy_runtime.clone(),
1875        summary: DsaSignalSummary {
1876            config: config.clone(),
1877            weights: weights.clone(),
1878            primary_run_signal: run_signals.primary_run_signal.clone(),
1879            analyzable_feature_count: analyzable_feature_count_override.unwrap_or_else(|| {
1880                nominal
1881                    .features
1882                    .iter()
1883                    .filter(|feature| feature.analyzable)
1884                    .count()
1885            }),
1886            alert_point_count,
1887            alert_run_count,
1888            numeric_alert_point_count,
1889            numeric_alert_run_count,
1890            watch_point_count,
1891            review_point_count,
1892            escalate_point_count,
1893            silenced_point_count,
1894            rescued_point_count,
1895            rescued_watch_to_review_points,
1896            rescued_review_to_escalate_points,
1897            failure_runs: failure_indices.len(),
1898            failure_run_recall,
1899            failure_run_recall_rate: dsa_row.failure_run_recall_rate,
1900            numeric_primary_failure_run_recall: numeric_dsa_row.failure_run_recall,
1901            mean_lead_time_runs: dsa_row.mean_lead_time_runs,
1902            median_lead_time_runs: dsa_row.median_lead_time_runs,
1903            pass_run_nuisance_proxy: dsa_row.pass_run_nuisance_proxy,
1904            numeric_primary_pass_run_nuisance_proxy: numeric_dsa_row.pass_run_nuisance_proxy,
1905            mean_lead_delta_vs_cusum_runs: dsa_row.mean_lead_delta_vs_cusum_runs,
1906            mean_lead_delta_vs_run_energy_runs: dsa_row.mean_lead_delta_vs_run_energy_runs,
1907            mean_lead_delta_vs_pca_fdc_runs: dsa_row.mean_lead_delta_vs_pca_fdc_runs,
1908            mean_lead_delta_vs_threshold_runs: dsa_row.mean_lead_delta_vs_threshold_runs,
1909            mean_lead_delta_vs_ewma_runs: dsa_row.mean_lead_delta_vs_ewma_runs,
1910            raw_boundary_nuisance_proxy: dsfb_raw_boundary_row.pass_run_nuisance_proxy,
1911            raw_boundary_episode_count: episode_summary.raw_boundary_episode_count,
1912            dsa_episode_count: episode_summary.dsa_episode_count,
1913            dsa_episodes_preceding_failure: episode_summary.dsa_episodes_preceding_failure,
1914            mean_dsa_episode_length_runs: episode_summary.mean_dsa_episode_length_runs,
1915            max_dsa_episode_length_runs: episode_summary.max_dsa_episode_length_runs,
1916            compression_ratio: episode_summary.compression_ratio,
1917            precursor_quality: episode_summary.precursor_quality,
1918            non_escalating_dsa_episode_fraction: episode_summary
1919                .non_escalating_dsa_episode_fraction,
1920            threshold_recall_gate_passed,
1921            boundary_nuisance_gate_passed,
1922            primary_success_condition_met,
1923            any_metric_improved,
1924            validation_passed,
1925            success_condition_failures,
1926            validation_failures,
1927        },
1928        comparison_summary,
1929        motif_policy_contributions,
1930        per_failure_run_signals,
1931    }
1932}
1933
1934fn motif_contribution_state(
1935    policy: HeuristicPolicyDefinition,
1936    feature_override: Option<&FeaturePolicyOverride>,
1937    flags: &[bool],
1938    run_index: usize,
1939    structural_active: bool,
1940    numeric_alert: bool,
1941    local_corroboration: bool,
1942    raw_violation_recent: bool,
1943) -> Option<MotifContributionState> {
1944    let minimum_window = feature_override
1945        .and_then(|override_entry| override_entry.minimum_window_override)
1946        .unwrap_or(policy.minimum_window);
1947    let minimum_hits = feature_override
1948        .and_then(|override_entry| override_entry.minimum_hits_override)
1949        .unwrap_or(policy.minimum_hits);
1950    let maximum_allowed_fragmentation = feature_override
1951        .and_then(|override_entry| override_entry.maximum_allowed_fragmentation_override)
1952        .unwrap_or_else(|| policy.maximum_allowed_fragmentation());
1953    let alert_class_default = feature_override
1954        .and_then(|override_entry| override_entry.alert_class_override)
1955        .unwrap_or(policy.alert_class_default);
1956    let requires_persistence = feature_override
1957        .and_then(|override_entry| override_entry.requires_persistence_override)
1958        .unwrap_or(policy.requires_persistence);
1959    let requires_corroboration = feature_override
1960        .and_then(|override_entry| override_entry.requires_corroboration_override)
1961        .unwrap_or(policy.requires_corroboration);
1962    let allow_watch_only = feature_override
1963        .and_then(|override_entry| override_entry.allow_watch_only)
1964        .unwrap_or(false);
1965    let suppress_if_isolated = feature_override
1966        .and_then(|override_entry| override_entry.suppress_if_isolated)
1967        .unwrap_or(false);
1968    let start = run_index.saturating_sub(minimum_window.saturating_sub(1));
1969    let hits = flags[start..=run_index]
1970        .iter()
1971        .filter(|flag| **flag)
1972        .count();
1973    if hits == 0 {
1974        return None;
1975    }
1976
1977    let fragmentation_proxy = episode_ranges(&flags[start..=run_index]).len() as f64 / hits as f64;
1978    let policy_active =
1979        hits >= minimum_hits && fragmentation_proxy <= maximum_allowed_fragmentation;
1980    let suppressed_to_silent = structural_active && !policy_active;
1981    let mut contribution_state = if !structural_active || !policy_active {
1982        DsaPolicyState::Silent
1983    } else {
1984        match alert_class_default {
1985            HeuristicAlertClass::Silent => {
1986                if policy.promotes_alert && numeric_alert && local_corroboration {
1987                    if raw_violation_recent {
1988                        DsaPolicyState::Review
1989                    } else {
1990                        DsaPolicyState::Watch
1991                    }
1992                } else {
1993                    DsaPolicyState::Silent
1994                }
1995            }
1996            HeuristicAlertClass::Watch => {
1997                if requires_persistence && !numeric_alert {
1998                    DsaPolicyState::Silent
1999                } else if requires_corroboration && !local_corroboration {
2000                    if policy.suppresses_alert {
2001                        DsaPolicyState::Silent
2002                    } else {
2003                        DsaPolicyState::Watch
2004                    }
2005                } else if policy.promotes_alert && numeric_alert && hits > minimum_hits {
2006                    DsaPolicyState::Review
2007                } else {
2008                    DsaPolicyState::Watch
2009                }
2010            }
2011            HeuristicAlertClass::Review => {
2012                if requires_persistence && !numeric_alert {
2013                    if policy.suppresses_alert {
2014                        DsaPolicyState::Silent
2015                    } else {
2016                        DsaPolicyState::Watch
2017                    }
2018                } else {
2019                    DsaPolicyState::Review
2020                }
2021            }
2022            HeuristicAlertClass::Escalate => {
2023                if numeric_alert && local_corroboration {
2024                    DsaPolicyState::Escalate
2025                } else if numeric_alert {
2026                    DsaPolicyState::Review
2027                } else {
2028                    DsaPolicyState::Watch
2029                }
2030            }
2031        }
2032    };
2033
2034    if contribution_state == DsaPolicyState::Review
2035        && policy.promotes_alert
2036        && numeric_alert
2037        && local_corroboration
2038        && raw_violation_recent
2039    {
2040        contribution_state = DsaPolicyState::Escalate;
2041    }
2042    if allow_watch_only && contribution_state > DsaPolicyState::Watch {
2043        contribution_state = DsaPolicyState::Watch;
2044    }
2045    if suppress_if_isolated && !local_corroboration {
2046        contribution_state = DsaPolicyState::Silent;
2047    }
2048
2049    Some(MotifContributionState {
2050        motif_name: policy.motif_name,
2051        default_alert_class: alert_class_default,
2052        contribution_state,
2053        fragmentation_proxy,
2054        suppressed_to_silent,
2055    })
2056}
2057
2058fn apply_recall_rescue(
2059    feature_override: Option<&FeaturePolicyOverride>,
2060    rescue_config: &RecallRescueConfig,
2061    config: &DsaConfig,
2062    resolved_alert_class: &[HeuristicAlertClass],
2063    policy_state: &mut [DsaPolicyState],
2064    dsa_alert: &mut [bool],
2065    semantic_rescue_support: &[bool],
2066    fragmentation_proxy_w: &[f64],
2067    dsa_score: &[f64],
2068    boundary_density_w: &[f64],
2069    ewma_occupancy_w: &[f64],
2070    motif_recurrence_w: &[f64],
2071    consistent: &[bool],
2072    run_index: usize,
2073) -> Option<&'static str> {
2074    let override_entry = feature_override?;
2075    if !override_entry.rescue_eligible {
2076        return None;
2077    }
2078    if policy_state[run_index].is_review_or_escalate() {
2079        return None;
2080    }
2081    let semantic_support_active = semantic_rescue_support
2082        .get(run_index)
2083        .copied()
2084        .unwrap_or(false);
2085    let silent_semantic_support_rescue = override_entry.rescue_priority >= 3
2086        && matches!(resolved_alert_class[run_index], HeuristicAlertClass::Silent)
2087        && semantic_support_active;
2088    let resolved_alert_is_rescuable = matches!(
2089        resolved_alert_class[run_index],
2090        HeuristicAlertClass::Watch | HeuristicAlertClass::Review
2091    );
2092    if !resolved_alert_is_rescuable && !silent_semantic_support_rescue {
2093        return None;
2094    }
2095    let minimum_window = override_entry
2096        .minimum_window_override
2097        .unwrap_or(config.window);
2098    let minimum_hits = override_entry.minimum_hits_override.unwrap_or(3);
2099    let fragmentation_ceiling = override_entry
2100        .maximum_allowed_fragmentation_override
2101        .unwrap_or(0.5);
2102    let score_margin = if override_entry.rescue_priority >= 3 {
2103        rescue_config.priority_three_score_margin
2104    } else if override_entry.rescue_priority >= 2 {
2105        rescue_config.priority_two_score_margin
2106    } else {
2107        rescue_config.priority_one_score_margin
2108    };
2109    let recent_start = run_index.saturating_sub(minimum_window.saturating_sub(1));
2110    let recent_watch_hits = (recent_start..=run_index)
2111        .filter(|&index| matches!(resolved_alert_class[index], HeuristicAlertClass::Watch))
2112        .count();
2113    let recent_semantic_support_hits = semantic_rescue_support[recent_start..=run_index]
2114        .iter()
2115        .filter(|flag| **flag)
2116        .count();
2117    let score_floor = (config.alert_tau - score_margin).max(0.0);
2118    let consistency_satisfied = consistent[run_index]
2119        || (override_entry.rescue_priority >= 2
2120            && matches!(resolved_alert_class[run_index], HeuristicAlertClass::Watch));
2121    let support_hits_satisfied =
2122        if override_entry.rescue_priority >= 3 && !resolved_alert_is_rescuable {
2123            recent_semantic_support_hits >= minimum_hits
2124        } else {
2125            recent_watch_hits >= minimum_hits
2126        };
2127    let structural_density_satisfied = if silent_semantic_support_rescue {
2128        semantic_support_active
2129            || (boundary_density_w[run_index] >= rescue_config.minimum_boundary_density
2130                && motif_recurrence_w[run_index] >= rescue_config.minimum_motif_recurrence)
2131    } else {
2132        boundary_density_w[run_index] >= rescue_config.minimum_boundary_density
2133            && motif_recurrence_w[run_index] >= rescue_config.minimum_motif_recurrence
2134    };
2135
2136    if !consistency_satisfied
2137        || !support_hits_satisfied
2138        || fragmentation_proxy_w[run_index] > fragmentation_ceiling
2139        || !structural_density_satisfied
2140        || ewma_occupancy_w[run_index] < rescue_config.minimum_ewma_occupancy
2141        || dsa_score[run_index] < score_floor
2142    {
2143        return None;
2144    }
2145
2146    if policy_state[run_index] == DsaPolicyState::Silent {
2147        if override_entry.allow_watch_only.unwrap_or(false) {
2148            policy_state[run_index] = DsaPolicyState::Watch;
2149            return Some("silent_to_watch");
2150        }
2151        if override_entry
2152            .allow_review_without_escalate
2153            .unwrap_or(false)
2154        {
2155            policy_state[run_index] = DsaPolicyState::Review;
2156            dsa_alert[run_index] = true;
2157            return Some(if resolved_alert_is_rescuable {
2158                "watch_to_review"
2159            } else {
2160                "silent_to_review"
2161            });
2162        }
2163    }
2164
2165    if policy_state[run_index] == DsaPolicyState::Review
2166        && !override_entry
2167            .allow_review_without_escalate
2168            .unwrap_or(false)
2169    {
2170        policy_state[run_index] = DsaPolicyState::Escalate;
2171        dsa_alert[run_index] = true;
2172        return Some("review_to_escalate");
2173    }
2174
2175    None
2176}
2177
2178fn dominant_alert_class(contributions: &[MotifContributionState]) -> HeuristicAlertClass {
2179    contributions
2180        .iter()
2181        .map(|contribution| contribution.default_alert_class)
2182        .max()
2183        .unwrap_or(HeuristicAlertClass::Silent)
2184}
2185
2186fn dominant_policy_state(contributions: &[MotifContributionState]) -> DsaPolicyState {
2187    contributions
2188        .iter()
2189        .map(|contribution| contribution.contribution_state)
2190        .max()
2191        .unwrap_or(DsaPolicyState::Silent)
2192}
2193
2194fn normalize_to_threshold(value: f64, threshold: f64) -> f64 {
2195    if threshold <= 0.0 {
2196        0.0
2197    } else {
2198        (value / threshold).clamp(0.0, 1.0)
2199    }
2200}
2201
2202fn bool_prefix_sum(flags: &[bool]) -> Vec<usize> {
2203    let mut prefix = Vec::with_capacity(flags.len() + 1);
2204    prefix.push(0);
2205    let mut total = 0usize;
2206    for flag in flags {
2207        total += usize::from(*flag);
2208        prefix.push(total);
2209    }
2210    prefix
2211}
2212
2213fn window_count(prefix: &[usize], start: usize, end: usize) -> usize {
2214    prefix[end + 1] - prefix[start]
2215}
2216
2217fn window_fraction(prefix: &[usize], start: usize, end: usize, window_len: f64) -> f64 {
2218    window_count(prefix, start, end) as f64 / window_len
2219}
2220
2221fn window_fraction_nonimputed(
2222    hit_prefix: &[usize],
2223    non_imputed_prefix: &[usize],
2224    start: usize,
2225    end: usize,
2226) -> f64 {
2227    let total_non_imputed = window_count(non_imputed_prefix, start, end);
2228    if total_non_imputed == 0 {
2229        0.0
2230    } else {
2231        window_count(hit_prefix, start, end) as f64 / total_non_imputed as f64
2232    }
2233}
2234
2235fn window_mean(values: &[f64], start: usize, end: usize) -> f64 {
2236    let slice = &values[start..=end];
2237    slice.iter().sum::<f64>() / slice.len() as f64
2238}
2239
2240fn window_is_consistent(drift: &[f64], drift_threshold: f64, start: usize, end: usize) -> bool {
2241    let mut previous_thresholded = 0i8;
2242
2243    for run_index in start..=end {
2244        let thresholded_sign = if drift[run_index] >= drift_threshold {
2245            1
2246        } else if drift[run_index] <= -drift_threshold {
2247            -1
2248        } else {
2249            0
2250        };
2251        if thresholded_sign < 0 {
2252            return false;
2253        }
2254        if thresholded_sign != 0 {
2255            if previous_thresholded != 0 && thresholded_sign != previous_thresholded {
2256                return false;
2257            }
2258            previous_thresholded = thresholded_sign;
2259        }
2260    }
2261
2262    true
2263}
2264
2265fn persistence_mask(values: &[bool], persistence_runs: usize) -> Vec<bool> {
2266    let mut out = Vec::with_capacity(values.len());
2267    let mut consecutive = 0usize;
2268    for value in values {
2269        if *value {
2270            consecutive += 1;
2271            out.push(consecutive >= persistence_runs);
2272        } else {
2273            consecutive = 0;
2274            out.push(false);
2275        }
2276    }
2277    out
2278}
2279
2280fn build_run_signals(
2281    traces: &[DsaFeatureTrace],
2282    raw_violation_run_signal: &[bool],
2283    config: &DsaConfig,
2284    run_count: usize,
2285) -> DsaRunSignals {
2286    let mut any_feature_dsa_alert = Vec::with_capacity(run_count);
2287    let mut primary_run_alert = Vec::with_capacity(run_count);
2288    let mut feature_count_dsa_alert = Vec::with_capacity(run_count);
2289    let mut watch_feature_count = Vec::with_capacity(run_count);
2290    let mut review_feature_count = Vec::with_capacity(run_count);
2291    let mut escalate_feature_count = Vec::with_capacity(run_count);
2292    let mut strict_escalate_run_alert = Vec::with_capacity(run_count);
2293    let mut numeric_primary_run_alert = Vec::with_capacity(run_count);
2294    let mut numeric_feature_count_dsa_alert = Vec::with_capacity(run_count);
2295
2296    for run_index in 0..run_count {
2297        let review_count = traces
2298            .iter()
2299            .filter(|trace| trace.dsa_alert[run_index])
2300            .count();
2301        let watch_count = traces
2302            .iter()
2303            .filter(|trace| trace.policy_state[run_index] == DsaPolicyState::Watch)
2304            .count();
2305        let escalate_count = traces
2306            .iter()
2307            .filter(|trace| trace.policy_state[run_index] == DsaPolicyState::Escalate)
2308            .count();
2309        let numeric_count = traces
2310            .iter()
2311            .filter(|trace| trace.numeric_dsa_alert[run_index])
2312            .count();
2313        feature_count_dsa_alert.push(review_count);
2314        watch_feature_count.push(watch_count);
2315        review_feature_count.push(review_count.saturating_sub(escalate_count));
2316        escalate_feature_count.push(escalate_count);
2317        numeric_feature_count_dsa_alert.push(numeric_count);
2318        strict_escalate_run_alert.push(escalate_count >= config.corroborating_feature_count_min);
2319        numeric_primary_run_alert.push(numeric_count >= config.corroborating_feature_count_min);
2320        let any_dsa = review_count > 0;
2321        any_feature_dsa_alert.push(any_dsa);
2322        primary_run_alert.push(review_count >= config.corroborating_feature_count_min);
2323    }
2324
2325    DsaRunSignals {
2326        primary_run_signal: format!(
2327            "feature_count_review_or_escalate(k) >= {}",
2328            config.corroborating_feature_count_min
2329        ),
2330        corroborating_feature_count_min: config.corroborating_feature_count_min,
2331        primary_run_alert,
2332        any_feature_dsa_alert,
2333        any_feature_raw_violation: raw_violation_run_signal.to_vec(),
2334        feature_count_dsa_alert,
2335        watch_feature_count,
2336        review_feature_count,
2337        escalate_feature_count,
2338        strict_escalate_run_alert,
2339        numeric_primary_run_alert,
2340        numeric_feature_count_dsa_alert,
2341    }
2342}
2343
2344fn compute_episode_summary(
2345    primary_signal_name: &str,
2346    dsa_signal: &[bool],
2347    raw_boundary_episode_count: usize,
2348    raw_violation_signal: &[bool],
2349    failure_window_mask: &[bool],
2350) -> DsaEpisodeSummary {
2351    let dsa_episodes = episode_ranges(dsa_signal);
2352    let dsa_lengths = dsa_episodes
2353        .iter()
2354        .map(|(start, end)| end - start + 1)
2355        .collect::<Vec<_>>();
2356    let dsa_episodes_preceding_failure = dsa_episodes
2357        .iter()
2358        .filter(|(start, end)| (*start..=*end).any(|run| failure_window_mask[run]))
2359        .count();
2360    let non_escalating_dsa_episode_fraction = if dsa_episodes.is_empty() {
2361        None
2362    } else {
2363        Some(
2364            dsa_episodes
2365                .iter()
2366                .filter(|(start, end)| !(*start..=*end).any(|run| raw_violation_signal[run]))
2367                .count() as f64
2368                / dsa_episodes.len() as f64,
2369        )
2370    };
2371
2372    DsaEpisodeSummary {
2373        primary_signal: primary_signal_name.into(),
2374        raw_boundary_episode_count,
2375        dsa_episode_count: dsa_episodes.len(),
2376        dsa_episodes_preceding_failure,
2377        mean_dsa_episode_length_runs: mean_usize(&dsa_lengths),
2378        max_dsa_episode_length_runs: dsa_lengths.iter().copied().max().unwrap_or(0),
2379        compression_ratio: if dsa_episodes.is_empty() {
2380            None
2381        } else {
2382            Some(raw_boundary_episode_count as f64 / dsa_episodes.len() as f64)
2383        },
2384        precursor_quality: if dsa_episodes.is_empty() {
2385            None
2386        } else {
2387            Some(dsa_episodes_preceding_failure as f64 / dsa_episodes.len() as f64)
2388        },
2389        non_escalating_dsa_episode_fraction,
2390    }
2391}
2392
2393fn episode_ranges(signal: &[bool]) -> Vec<(usize, usize)> {
2394    let mut episodes = Vec::new();
2395    let mut current_start: Option<usize> = None;
2396
2397    for (run_index, flag) in signal.iter().copied().enumerate() {
2398        match (current_start, flag) {
2399            (None, true) => current_start = Some(run_index),
2400            (Some(start), false) => {
2401                episodes.push((start, run_index - 1));
2402                current_start = None;
2403            }
2404            _ => {}
2405        }
2406    }
2407
2408    if let Some(start) = current_start {
2409        episodes.push((start, signal.len().saturating_sub(1)));
2410    }
2411
2412    episodes
2413}
2414
2415#[derive(Debug, Clone)]
2416struct EarliestPrimarySignal {
2417    run_index: usize,
2418    feature_index: usize,
2419    feature_name: String,
2420    score: f64,
2421    source: String,
2422}
2423
2424fn earliest_primary_signal(
2425    traces: &[DsaFeatureTrace],
2426    primary_signal: &[bool],
2427    start: usize,
2428    end: usize,
2429) -> Option<EarliestPrimarySignal> {
2430    let run_index = earliest_run_signal(primary_signal, start, end)?;
2431    traces
2432        .iter()
2433        .filter(|trace| trace.dsa_alert[run_index])
2434        .map(|trace| EarliestPrimarySignal {
2435            run_index,
2436            feature_index: trace.feature_index,
2437            feature_name: trace.feature_name.clone(),
2438            score: trace.dsa_score[run_index],
2439            source: "DSA".into(),
2440        })
2441        .max_by(|left, right| {
2442            left.score
2443                .partial_cmp(&right.score)
2444                .unwrap_or(std::cmp::Ordering::Equal)
2445                .then_with(|| right.feature_index.cmp(&left.feature_index))
2446        })
2447}
2448
2449fn build_failure_window_mask(
2450    run_count: usize,
2451    failure_indices: &[usize],
2452    pre_failure_lookback_runs: usize,
2453) -> Vec<bool> {
2454    let mut mask = vec![false; run_count];
2455    for &failure_index in failure_indices {
2456        let start = failure_index.saturating_sub(pre_failure_lookback_runs);
2457        for slot in &mut mask[start..failure_index] {
2458            *slot = true;
2459        }
2460    }
2461    mask
2462}
2463
2464#[derive(Debug, Clone)]
2465struct MaxDsaScore {
2466    feature_index: usize,
2467    feature_name: String,
2468    run_index: usize,
2469    score: f64,
2470    boundary_density_w: f64,
2471    drift_persistence_w: f64,
2472    slew_density_w: f64,
2473    ewma_occupancy_w: f64,
2474    motif_recurrence_w: f64,
2475    fragmentation_proxy_w: f64,
2476    consistent: bool,
2477    policy_state: DsaPolicyState,
2478    resolved_alert_class: HeuristicAlertClass,
2479    numeric_dsa_alert: bool,
2480    dsa_alert: bool,
2481    policy_suppressed_to_silent: bool,
2482    rescue_transition: String,
2483}
2484
2485fn max_dsa_score(traces: &[DsaFeatureTrace], start: usize, end: usize) -> Option<MaxDsaScore> {
2486    let mut max_score: Option<MaxDsaScore> = None;
2487    for trace in traces {
2488        for run_index in start..end {
2489            let candidate = MaxDsaScore {
2490                feature_index: trace.feature_index,
2491                feature_name: trace.feature_name.clone(),
2492                run_index,
2493                score: trace.dsa_score[run_index],
2494                boundary_density_w: trace.boundary_density_w[run_index],
2495                drift_persistence_w: trace.drift_persistence_w[run_index],
2496                slew_density_w: trace.slew_density_w[run_index],
2497                ewma_occupancy_w: trace.ewma_occupancy_w[run_index],
2498                motif_recurrence_w: trace.motif_recurrence_w[run_index],
2499                fragmentation_proxy_w: trace.fragmentation_proxy_w[run_index],
2500                consistent: trace.consistent[run_index],
2501                policy_state: trace.policy_state[run_index],
2502                resolved_alert_class: trace.resolved_alert_class[run_index],
2503                numeric_dsa_alert: trace.numeric_dsa_alert[run_index],
2504                dsa_alert: trace.dsa_alert[run_index],
2505                policy_suppressed_to_silent: trace.policy_suppressed_to_silent[run_index],
2506                rescue_transition: trace.rescue_transition[run_index].clone(),
2507            };
2508            let should_replace = match &max_score {
2509                None => true,
2510                Some(current) => {
2511                    candidate.score > current.score
2512                        || (candidate.score == current.score
2513                            && candidate.feature_index < current.feature_index)
2514                }
2515            };
2516            if should_replace {
2517                max_score = Some(candidate);
2518            }
2519        }
2520    }
2521    max_score
2522}
2523
2524fn earliest_run_signal(signal: &[bool], start: usize, end: usize) -> Option<usize> {
2525    (start..end).find(|&run_index| signal[run_index])
2526}
2527
2528fn paired_delta(left: Option<usize>, right: Option<usize>) -> Option<i64> {
2529    Some(left? as i64 - right? as i64)
2530}
2531
2532fn baseline_row(
2533    signal: &str,
2534    failure_runs: usize,
2535    lead_values: &[Option<usize>],
2536    nuisance: f64,
2537) -> SignalComparisonRow {
2538    let recall = lead_values.iter().filter(|value| value.is_some()).count();
2539    SignalComparisonRow {
2540        signal: signal.into(),
2541        failure_run_recall: recall,
2542        failure_runs,
2543        failure_run_recall_rate: rate(recall, failure_runs),
2544        mean_lead_time_runs: mean_option_usize(lead_values),
2545        median_lead_time_runs: median_option_usize(lead_values),
2546        pass_run_nuisance_proxy: nuisance,
2547        mean_lead_delta_vs_cusum_runs: None,
2548        mean_lead_delta_vs_run_energy_runs: None,
2549        mean_lead_delta_vs_pca_fdc_runs: None,
2550        mean_lead_delta_vs_threshold_runs: None,
2551        mean_lead_delta_vs_ewma_runs: None,
2552    }
2553}
2554
2555fn component_contributions(traces: &[DsaFeatureTrace]) -> Vec<DsaComponentContribution> {
2556    let total_points = traces
2557        .iter()
2558        .map(|trace| trace.dsa_score.len())
2559        .sum::<usize>()
2560        .max(1);
2561    let alert_points = traces
2562        .iter()
2563        .map(|trace| trace.dsa_alert.iter().filter(|flag| **flag).count())
2564        .sum::<usize>()
2565        .max(1);
2566
2567    let component_rows = [
2568        (
2569            "boundary_density_W",
2570            traces
2571                .iter()
2572                .flat_map(|trace| trace.boundary_density_w.iter().copied())
2573                .sum::<f64>(),
2574            traces
2575                .iter()
2576                .map(|trace| {
2577                    trace
2578                        .boundary_density_w
2579                        .iter()
2580                        .zip(&trace.dsa_alert)
2581                        .filter_map(|(value, flag)| flag.then_some(*value))
2582                        .sum::<f64>()
2583                })
2584                .sum::<f64>(),
2585        ),
2586        (
2587            "drift_persistence_W",
2588            traces
2589                .iter()
2590                .flat_map(|trace| trace.drift_persistence_w.iter().copied())
2591                .sum::<f64>(),
2592            traces
2593                .iter()
2594                .map(|trace| {
2595                    trace
2596                        .drift_persistence_w
2597                        .iter()
2598                        .zip(&trace.dsa_alert)
2599                        .filter_map(|(value, flag)| flag.then_some(*value))
2600                        .sum::<f64>()
2601                })
2602                .sum::<f64>(),
2603        ),
2604        (
2605            "slew_density_W",
2606            traces
2607                .iter()
2608                .flat_map(|trace| trace.slew_density_w.iter().copied())
2609                .sum::<f64>(),
2610            traces
2611                .iter()
2612                .map(|trace| {
2613                    trace
2614                        .slew_density_w
2615                        .iter()
2616                        .zip(&trace.dsa_alert)
2617                        .filter_map(|(value, flag)| flag.then_some(*value))
2618                        .sum::<f64>()
2619                })
2620                .sum::<f64>(),
2621        ),
2622        (
2623            "ewma_occupancy_W",
2624            traces
2625                .iter()
2626                .flat_map(|trace| trace.ewma_occupancy_w.iter().copied())
2627                .sum::<f64>(),
2628            traces
2629                .iter()
2630                .map(|trace| {
2631                    trace
2632                        .ewma_occupancy_w
2633                        .iter()
2634                        .zip(&trace.dsa_alert)
2635                        .filter_map(|(value, flag)| flag.then_some(*value))
2636                        .sum::<f64>()
2637                })
2638                .sum::<f64>(),
2639        ),
2640        (
2641            "motif_recurrence_W",
2642            traces
2643                .iter()
2644                .flat_map(|trace| trace.motif_recurrence_w.iter().copied())
2645                .sum::<f64>(),
2646            traces
2647                .iter()
2648                .map(|trace| {
2649                    trace
2650                        .motif_recurrence_w
2651                        .iter()
2652                        .zip(&trace.dsa_alert)
2653                        .filter_map(|(value, flag)| flag.then_some(*value))
2654                        .sum::<f64>()
2655                })
2656                .sum::<f64>(),
2657        ),
2658    ];
2659
2660    let mut out = component_rows
2661        .into_iter()
2662        .map(|(component, all_sum, alert_sum)| DsaComponentContribution {
2663            component: component.into(),
2664            mean_value_on_alert_points: alert_sum / alert_points as f64,
2665            mean_value_on_all_points: all_sum / total_points as f64,
2666            total_value_on_alert_points: alert_sum,
2667        })
2668        .collect::<Vec<_>>();
2669    out.sort_by(|left, right| {
2670        right
2671            .mean_value_on_alert_points
2672            .partial_cmp(&left.mean_value_on_alert_points)
2673            .unwrap_or(std::cmp::Ordering::Equal)
2674            .then_with(|| left.component.cmp(&right.component))
2675    });
2676    out
2677}
2678
2679fn aggregate_motif_policy_contributions(
2680    traces: &[DsaFeatureTrace],
2681) -> Vec<DsaMotifPolicyContribution> {
2682    let mut aggregated = BTreeMap::<String, DsaMotifPolicyContribution>::new();
2683    for trace in traces {
2684        for contribution in &trace.motif_policy_contributions {
2685            let entry = aggregated
2686                .entry(contribution.motif_name.clone())
2687                .or_insert_with(|| DsaMotifPolicyContribution {
2688                    motif_name: contribution.motif_name.clone(),
2689                    alert_class_default: contribution.alert_class_default,
2690                    watch_points: 0,
2691                    review_points: 0,
2692                    escalate_points: 0,
2693                    silent_suppression_points: 0,
2694                    pass_review_or_escalate_points: 0,
2695                    pre_failure_review_or_escalate_points: 0,
2696                });
2697            entry.watch_points += contribution.watch_points;
2698            entry.review_points += contribution.review_points;
2699            entry.escalate_points += contribution.escalate_points;
2700            entry.silent_suppression_points += contribution.silent_suppression_points;
2701            entry.pass_review_or_escalate_points += contribution.pass_review_or_escalate_points;
2702            entry.pre_failure_review_or_escalate_points +=
2703                contribution.pre_failure_review_or_escalate_points;
2704        }
2705    }
2706
2707    aggregated.into_values().collect::<Vec<_>>()
2708}
2709
2710fn success_condition_failures(
2711    dsa: &SignalComparisonRow,
2712    threshold: &SignalComparisonRow,
2713    ewma: &SignalComparisonRow,
2714) -> Vec<String> {
2715    let mut failures = Vec::new();
2716    if dsa.pass_run_nuisance_proxy >= ewma.pass_run_nuisance_proxy {
2717        failures.push(format!(
2718            "pass-run nuisance {:.4} is not below EWMA nuisance {:.4}",
2719            dsa.pass_run_nuisance_proxy, ewma.pass_run_nuisance_proxy
2720        ));
2721    }
2722    let recall_floor = threshold
2723        .failure_run_recall
2724        .saturating_sub(DSA_PRIMARY_SUCCESS_RECALL_TOLERANCE_RUNS);
2725    if dsa.failure_run_recall < recall_floor {
2726        failures.push(format!(
2727            "failure recall {}/{} is more than {} run(s) below threshold recall {}/{}",
2728            dsa.failure_run_recall,
2729            dsa.failure_runs,
2730            DSA_PRIMARY_SUCCESS_RECALL_TOLERANCE_RUNS,
2731            threshold.failure_run_recall,
2732            threshold.failure_runs,
2733        ));
2734    }
2735    failures
2736}
2737
2738fn validation_failures(
2739    dsa: &SignalComparisonRow,
2740    threshold: &SignalComparisonRow,
2741    raw_boundary_nuisance_proxy: f64,
2742    threshold_recall_gate_passed: bool,
2743    boundary_nuisance_gate_passed: bool,
2744    any_metric_improved: bool,
2745) -> Vec<String> {
2746    let mut failures = Vec::new();
2747    if !threshold_recall_gate_passed {
2748        failures.push(format!(
2749            "failure recall {}/{} is below threshold recall {}/{}",
2750            dsa.failure_run_recall,
2751            dsa.failure_runs,
2752            threshold.failure_run_recall,
2753            threshold.failure_runs,
2754        ));
2755    }
2756    if !boundary_nuisance_gate_passed {
2757        failures.push(format!(
2758            "pass-run nuisance {:.4} is not below raw DSFB boundary nuisance {:.4}",
2759            dsa.pass_run_nuisance_proxy, raw_boundary_nuisance_proxy,
2760        ));
2761    }
2762    if !any_metric_improved {
2763        failures.push(
2764            "no saved DSA metric improves nuisance, lead time, recall, or compression relative to the logged baselines"
2765                .into(),
2766        );
2767    }
2768    failures
2769}
2770
2771fn dsa_conclusion(
2772    dsa: &SignalComparisonRow,
2773    threshold: &SignalComparisonRow,
2774    ewma: &SignalComparisonRow,
2775    dsfb_violation: &SignalComparisonRow,
2776    raw_boundary: &SignalComparisonRow,
2777    episode_summary: &DsaEpisodeSummary,
2778    primary_success_condition_met: bool,
2779    nuisance_improved: bool,
2780    lead_time_improved: bool,
2781    recall_preserved: bool,
2782    compression_improved: bool,
2783    nothing_improved: bool,
2784    component_contributions: &[DsaComponentContribution],
2785    success_condition_failures: &[String],
2786    validation_failures: &[String],
2787) -> String {
2788    let top_components = component_contributions
2789        .iter()
2790        .take(3)
2791        .map(|row| format!("{}={:.4}", row.component, row.mean_value_on_alert_points))
2792        .collect::<Vec<_>>()
2793        .join(", ");
2794
2795    if primary_success_condition_met {
2796        if !validation_failures.is_empty() {
2797            return format!(
2798                "DSA satisfies the crate's legacy one-run-tolerance nuisance/recall sweep gate: pass-run nuisance {:.4} is below EWMA nuisance {:.4}, failure recall is {}/{}, mean lead deltas are threshold={} and EWMA={}, precursor quality is {}, and compression ratio is {}. It still fails the stricter validation gates ({}), and this legacy sweep gate is not the stronger predeclared delta target used in the optimization report. No superiority claim is made and DSFB Violation remains the frozen instantaneous envelope-exit comparator.",
2799                dsa.pass_run_nuisance_proxy,
2800                ewma.pass_run_nuisance_proxy,
2801                dsa.failure_run_recall,
2802                dsa.failure_runs,
2803                format_option_f64(dsa.mean_lead_delta_vs_threshold_runs),
2804                format_option_f64(dsa.mean_lead_delta_vs_ewma_runs),
2805                format_option_f64(episode_summary.precursor_quality),
2806                format_option_f64(episode_summary.compression_ratio),
2807                validation_failures.join("; "),
2808            );
2809        }
2810        return format!(
2811            "DSA satisfies the crate's legacy one-run-tolerance nuisance/recall sweep gate: pass-run nuisance {:.4} is below EWMA nuisance {:.4}, failure recall is {}/{}, mean lead deltas are threshold={} and EWMA={}, precursor quality is {}, and compression ratio is {}. This legacy sweep gate is not the stronger predeclared delta target used in the optimization report. DSFB Violation remains the frozen instantaneous envelope-exit comparator.",
2812            dsa.pass_run_nuisance_proxy,
2813            ewma.pass_run_nuisance_proxy,
2814            dsa.failure_run_recall,
2815            dsa.failure_runs,
2816            format_option_f64(dsa.mean_lead_delta_vs_threshold_runs),
2817            format_option_f64(dsa.mean_lead_delta_vs_ewma_runs),
2818            format_option_f64(episode_summary.precursor_quality),
2819            format_option_f64(episode_summary.compression_ratio),
2820        );
2821    }
2822
2823    if !validation_failures.is_empty() {
2824        if nuisance_improved && !lead_time_improved {
2825            return format!(
2826                "DSA improves nuisance relative to at least one comparator but does not satisfy the primary success condition ({}) and also fails validation gates ({}). Recall is {}/{}, threshold recall is {}/{}, EWMA recall is {}/{}, DSFB Violation recall is {}/{}, raw-boundary nuisance delta is {:.4}, precursor quality is {}, compression ratio is {}, and the strongest DSA components were {}. No superiority claim is made.",
2827                success_condition_failures.join("; "),
2828                validation_failures.join("; "),
2829                dsa.failure_run_recall,
2830                dsa.failure_runs,
2831                threshold.failure_run_recall,
2832                threshold.failure_runs,
2833                ewma.failure_run_recall,
2834                ewma.failure_runs,
2835                dsfb_violation.failure_run_recall,
2836                dsfb_violation.failure_runs,
2837                dsa.pass_run_nuisance_proxy - raw_boundary.pass_run_nuisance_proxy,
2838                format_option_f64(episode_summary.precursor_quality),
2839                format_option_f64(episode_summary.compression_ratio),
2840                top_components,
2841            );
2842        }
2843
2844        if nothing_improved {
2845            return format!(
2846                "DSA fails to improve nuisance, lead time, recall, or compression and fails both the primary success condition ({}) and validation gates ({}). Recall is {}/{}, pass-run nuisance is {:.4}, mean lead deltas are threshold={} and EWMA={}, precursor quality is {}, compression ratio is {}, and the strongest DSA components were {}.",
2847                success_condition_failures.join("; "),
2848                validation_failures.join("; "),
2849                dsa.failure_run_recall,
2850                dsa.failure_runs,
2851                dsa.pass_run_nuisance_proxy,
2852                format_option_f64(dsa.mean_lead_delta_vs_threshold_runs),
2853                format_option_f64(dsa.mean_lead_delta_vs_ewma_runs),
2854                format_option_f64(episode_summary.precursor_quality),
2855                format_option_f64(episode_summary.compression_ratio),
2856                top_components,
2857            );
2858        }
2859
2860        return format!(
2861            "DSA shows mixed trade-offs but does not satisfy the primary success condition ({}) and fails validation gates ({}). Nuisance improved: {}, lead time improved: {}, recall preserved: {}, compression improved: {}, precursor quality is {}, and the strongest DSA components were {}. No superiority claim is made.",
2862            success_condition_failures.join("; "),
2863            validation_failures.join("; "),
2864            nuisance_improved,
2865            lead_time_improved,
2866            recall_preserved,
2867            compression_improved,
2868            format_option_f64(episode_summary.precursor_quality),
2869            top_components,
2870        );
2871    }
2872
2873    if nuisance_improved && !lead_time_improved {
2874        return format!(
2875            "DSA reduces nuisance relative to at least one comparator, but it does not satisfy the primary success condition ({}). Recall is {}/{}, threshold recall is {}/{}, pass-run nuisance is {:.4}, mean lead deltas are threshold={} and EWMA={}, precursor quality is {}, compression ratio is {}, and the strongest DSA components were {}. No superiority claim is made.",
2876            success_condition_failures.join("; "),
2877            dsa.failure_run_recall,
2878            dsa.failure_runs,
2879            threshold.failure_run_recall,
2880            threshold.failure_runs,
2881            dsa.pass_run_nuisance_proxy,
2882            format_option_f64(dsa.mean_lead_delta_vs_threshold_runs),
2883            format_option_f64(dsa.mean_lead_delta_vs_ewma_runs),
2884            format_option_f64(episode_summary.precursor_quality),
2885            format_option_f64(episode_summary.compression_ratio),
2886            top_components,
2887        );
2888    }
2889
2890    if nothing_improved {
2891        return format!(
2892            "DSA fails to improve nuisance, lead time, recall, or compression relative to the logged baselines, and it does not satisfy the primary success condition ({}). Recall is {}/{}, pass-run nuisance is {:.4}, mean lead deltas are threshold={} and EWMA={}, precursor quality is {}, compression ratio is {}, and the strongest DSA components were {}.",
2893            success_condition_failures.join("; "),
2894            dsa.failure_run_recall,
2895            dsa.failure_runs,
2896            dsa.pass_run_nuisance_proxy,
2897            format_option_f64(dsa.mean_lead_delta_vs_threshold_runs),
2898            format_option_f64(dsa.mean_lead_delta_vs_ewma_runs),
2899            format_option_f64(episode_summary.precursor_quality),
2900            format_option_f64(episode_summary.compression_ratio),
2901            top_components,
2902        );
2903    }
2904
2905    format!(
2906        "DSA shows mixed trade-offs without satisfying the primary success condition ({}). Recall preserved: {}, nuisance improved: {}, lead time improved: {}, precursor quality is {}, compression ratio is {}, and the strongest DSA components were {}.",
2907        success_condition_failures.join("; "),
2908        recall_preserved,
2909        nuisance_improved,
2910        lead_time_improved,
2911        format_option_f64(episode_summary.precursor_quality),
2912        format_option_f64(episode_summary.compression_ratio),
2913        top_components,
2914    )
2915}
2916
2917pub fn summarize_dsa_grid(rows: &[DsaCalibrationRow]) -> DsaGridSummary {
2918    let mut by_corroboration: BTreeMap<usize, Vec<DsaCalibrationRow>> = BTreeMap::new();
2919    for row in rows {
2920        by_corroboration
2921            .entry(row.corroborating_feature_count_min)
2922            .or_default()
2923            .push(row.clone());
2924    }
2925    let corroboration_summaries = by_corroboration
2926        .iter()
2927        .map(
2928            |(&corroborating_feature_count_min, grouped_rows)| DsaCorroborationSummary {
2929                corroborating_feature_count_min,
2930                representative_row: choose_closest_to_success(grouped_rows),
2931            },
2932        )
2933        .collect::<Vec<_>>();
2934    let success_row_count = rows
2935        .iter()
2936        .filter(|row| row.primary_success_condition_met)
2937        .count();
2938    DsaGridSummary {
2939        grid_point_count: rows.len(),
2940        optimization_priority_order: dsa_optimization_priority_order(),
2941        primary_success_condition_definition: dsa_primary_success_condition_definition(),
2942        success_row_count,
2943        any_success_row: success_row_count > 0,
2944        closest_to_success: choose_closest_to_success(rows),
2945        best_success_row: choose_best_success_row(rows),
2946        best_precursor_quality_row: choose_best_precursor_quality_row(rows),
2947        cross_feature_corroboration_effect: cross_feature_corroboration_effect(
2948            &corroboration_summaries,
2949        ),
2950        limiting_factor: limiting_factor(rows),
2951        corroboration_summaries,
2952    }
2953}
2954
2955fn choose_closest_to_success(rows: &[DsaCalibrationRow]) -> Option<DsaCalibrationRow> {
2956    if let Some(best_success_row) = choose_best_success_row(rows) {
2957        return Some(best_success_row);
2958    }
2959    rows.iter()
2960        .cloned()
2961        .max_by(compare_dsa_rows_by_success_closeness)
2962}
2963
2964fn choose_best_success_row(rows: &[DsaCalibrationRow]) -> Option<DsaCalibrationRow> {
2965    rows.iter()
2966        .filter(|row| row.primary_success_condition_met)
2967        .cloned()
2968        .max_by(compare_dsa_rows_by_priority)
2969}
2970
2971fn choose_best_precursor_quality_row(rows: &[DsaCalibrationRow]) -> Option<DsaCalibrationRow> {
2972    rows.iter().cloned().max_by(|left, right| {
2973        compare_option_f64(left.precursor_quality, right.precursor_quality)
2974            .then_with(|| compare_dsa_rows_by_priority(left, right))
2975    })
2976}
2977
2978fn compare_dsa_rows_by_priority(
2979    left: &DsaCalibrationRow,
2980    right: &DsaCalibrationRow,
2981) -> std::cmp::Ordering {
2982    compare_f64_lower_is_better(
2983        left.pass_run_nuisance_delta_vs_raw_boundary,
2984        right.pass_run_nuisance_delta_vs_raw_boundary,
2985    )
2986    .then_with(|| {
2987        compare_f64_lower_is_better(
2988            left.pass_run_nuisance_delta_vs_ewma,
2989            right.pass_run_nuisance_delta_vs_ewma,
2990        )
2991    })
2992    .then_with(|| left.failure_run_recall.cmp(&right.failure_run_recall))
2993    .then_with(|| {
2994        compare_option_f64(
2995            averaged_lead_delta(
2996                left.mean_lead_delta_vs_threshold_runs,
2997                left.mean_lead_delta_vs_ewma_runs,
2998            ),
2999            averaged_lead_delta(
3000                right.mean_lead_delta_vs_threshold_runs,
3001                right.mean_lead_delta_vs_ewma_runs,
3002            ),
3003        )
3004    })
3005    .then_with(|| compare_option_f64(left.precursor_quality, right.precursor_quality))
3006    .then_with(|| compare_option_f64(left.compression_ratio, right.compression_ratio))
3007    .then_with(|| {
3008        right
3009            .corroborating_feature_count_min
3010            .cmp(&left.corroborating_feature_count_min)
3011    })
3012}
3013
3014fn compare_dsa_rows_by_success_closeness(
3015    left: &DsaCalibrationRow,
3016    right: &DsaCalibrationRow,
3017) -> std::cmp::Ordering {
3018    compare_usize_lower_is_better(
3019        unmet_primary_success_conditions(left),
3020        unmet_primary_success_conditions(right),
3021    )
3022    .then_with(|| {
3023        compare_usize_lower_is_better(
3024            recall_shortfall_vs_primary_success_floor(left),
3025            recall_shortfall_vs_primary_success_floor(right),
3026        )
3027    })
3028    .then_with(|| {
3029        compare_f64_lower_is_better(
3030            ewma_nuisance_gap_to_primary_success(left),
3031            ewma_nuisance_gap_to_primary_success(right),
3032        )
3033    })
3034    .then_with(|| compare_dsa_rows_by_priority(left, right))
3035}
3036
3037fn unmet_primary_success_conditions(row: &DsaCalibrationRow) -> usize {
3038    usize::from(ewma_nuisance_gap_to_primary_success(row) > 0.0)
3039        + usize::from(recall_shortfall_vs_primary_success_floor(row) > 0)
3040}
3041
3042fn recall_shortfall_vs_primary_success_floor(row: &DsaCalibrationRow) -> usize {
3043    let recall_floor = row
3044        .threshold_failure_run_recall
3045        .saturating_sub(DSA_PRIMARY_SUCCESS_RECALL_TOLERANCE_RUNS);
3046    recall_floor.saturating_sub(row.failure_run_recall)
3047}
3048
3049fn ewma_nuisance_gap_to_primary_success(row: &DsaCalibrationRow) -> f64 {
3050    row.pass_run_nuisance_delta_vs_ewma.max(0.0)
3051}
3052
3053fn compare_usize_lower_is_better(left: usize, right: usize) -> std::cmp::Ordering {
3054    right.cmp(&left)
3055}
3056
3057fn compare_f64_lower_is_better(left: f64, right: f64) -> std::cmp::Ordering {
3058    right
3059        .partial_cmp(&left)
3060        .unwrap_or(std::cmp::Ordering::Equal)
3061}
3062
3063fn compare_option_f64(left: Option<f64>, right: Option<f64>) -> std::cmp::Ordering {
3064    match (left, right) {
3065        (Some(left), Some(right)) => left
3066            .partial_cmp(&right)
3067            .unwrap_or(std::cmp::Ordering::Equal),
3068        (Some(_), None) => std::cmp::Ordering::Greater,
3069        (None, Some(_)) => std::cmp::Ordering::Less,
3070        (None, None) => std::cmp::Ordering::Equal,
3071    }
3072}
3073
3074fn averaged_lead_delta(threshold_delta: Option<f64>, ewma_delta: Option<f64>) -> Option<f64> {
3075    let mut values = Vec::new();
3076    if let Some(value) = threshold_delta {
3077        values.push(value);
3078    }
3079    if let Some(value) = ewma_delta {
3080        values.push(value);
3081    }
3082    (!values.is_empty()).then(|| values.iter().sum::<f64>() / values.len() as f64)
3083}
3084
3085fn cross_feature_corroboration_effect(
3086    corroboration_summaries: &[DsaCorroborationSummary],
3087) -> String {
3088    let Some(base) = corroboration_summaries
3089        .iter()
3090        .find(|summary| summary.corroborating_feature_count_min == 2)
3091        .and_then(|summary| summary.representative_row.as_ref())
3092    else {
3093        return "Cross-feature corroboration effect could not be determined from the saved grid."
3094            .into();
3095    };
3096
3097    let higher_rows = corroboration_summaries
3098        .iter()
3099        .filter(|summary| summary.corroborating_feature_count_min > 2)
3100        .filter_map(|summary| summary.representative_row.as_ref())
3101        .collect::<Vec<_>>();
3102    if higher_rows.is_empty() {
3103        return "Cross-feature corroboration effect could not be determined from the saved grid."
3104            .into();
3105    }
3106
3107    let all_lower_nuisance = higher_rows.iter().all(|row| {
3108        row.pass_run_nuisance_delta_vs_raw_boundary < base.pass_run_nuisance_delta_vs_raw_boundary
3109            && row.pass_run_nuisance_delta_vs_ewma < base.pass_run_nuisance_delta_vs_ewma
3110    });
3111    let any_lower_recall = higher_rows
3112        .iter()
3113        .any(|row| row.failure_run_recall < base.failure_run_recall);
3114    let any_higher_recall = higher_rows
3115        .iter()
3116        .any(|row| row.failure_run_recall > base.failure_run_recall);
3117
3118    if all_lower_nuisance && any_lower_recall {
3119        "Higher cross-feature corroboration reduced nuisance but degraded recall relative to m=2."
3120            .into()
3121    } else if all_lower_nuisance && !any_lower_recall {
3122        "Higher cross-feature corroboration reduced nuisance without reducing recall relative to m=2."
3123            .into()
3124    } else if !all_lower_nuisance && any_lower_recall {
3125        "Higher cross-feature corroboration degraded recall without a consistent nuisance benefit relative to m=2."
3126            .into()
3127    } else if any_higher_recall {
3128        "Higher cross-feature corroboration improved recall at some settings, but nuisance trade-offs remained mixed relative to m=2."
3129            .into()
3130    } else {
3131        "Cross-feature corroboration produced mixed nuisance and recall trade-offs across m=2,3,5."
3132            .into()
3133    }
3134}
3135
3136fn limiting_factor(rows: &[DsaCalibrationRow]) -> String {
3137    let any_recall_gate_passed = rows.iter().any(|row| row.threshold_recall_gate_passed);
3138    let any_ewma_nuisance_success = rows.iter().any(|row| {
3139        row.primary_success_condition_met
3140            || !row.success_condition_failures.contains("EWMA nuisance")
3141    });
3142    if !any_recall_gate_passed {
3143        "Recall was the limiting factor across the saved grid.".into()
3144    } else if !any_ewma_nuisance_success {
3145        "Nuisance relative to EWMA was the limiting factor across the saved grid.".into()
3146    } else if rows.iter().any(|row| row.primary_success_condition_met) {
3147        "The saved grid contains at least one row that satisfies the legacy one-run-tolerance nuisance/recall sweep gate."
3148            .into()
3149    } else {
3150        "Both nuisance and recall remained limiting factors across different parts of the saved grid.".into()
3151    }
3152}
3153
3154fn rate(count: usize, total: usize) -> f64 {
3155    if total == 0 {
3156        0.0
3157    } else {
3158        count as f64 / total as f64
3159    }
3160}
3161
3162fn count_present<I, T>(iter: I) -> usize
3163where
3164    I: Iterator<Item = Option<T>>,
3165{
3166    iter.filter(|value| value.is_some()).count()
3167}
3168
3169fn mean_usize(values: &[usize]) -> Option<f64> {
3170    (!values.is_empty()).then_some(values.iter().sum::<usize>() as f64 / values.len() as f64)
3171}
3172
3173fn mean_option_usize(values: &[Option<usize>]) -> Option<f64> {
3174    let present = values.iter().flatten().copied().collect::<Vec<_>>();
3175    mean_usize(&present)
3176}
3177
3178fn mean_option_i64(values: &[Option<i64>]) -> Option<f64> {
3179    let present = values.iter().flatten().copied().collect::<Vec<_>>();
3180    (!present.is_empty()).then_some(present.iter().sum::<i64>() as f64 / present.len() as f64)
3181}
3182
3183fn median_option_usize(values: &[Option<usize>]) -> Option<f64> {
3184    let mut present = values.iter().flatten().copied().collect::<Vec<_>>();
3185    if present.is_empty() {
3186        return None;
3187    }
3188    present.sort_unstable();
3189    let middle = present.len() / 2;
3190    if present.len() % 2 == 1 {
3191        Some(present[middle] as f64)
3192    } else {
3193        Some((present[middle - 1] + present[middle]) as f64 / 2.0)
3194    }
3195}
3196
3197fn format_option_f64(value: Option<f64>) -> String {
3198    value
3199        .map(|value| format!("{value:.4}"))
3200        .unwrap_or_else(|| "n/a".into())
3201}
3202
3203#[cfg(test)]
3204mod tests {
3205    use super::*;
3206
3207    #[test]
3208    fn dsa_persistence_gating_requires_consecutive_hits() {
3209        let alert = persistence_mask(&[false, true, true, false, true, true, true], 2);
3210        assert_eq!(alert, vec![false, false, true, false, false, true, true]);
3211    }
3212
3213    #[test]
3214    fn dsa_consistency_uses_thresholded_direction_regimes() {
3215        assert!(window_is_consistent(&[0.0, 0.2, 0.1, 0.0], 0.15, 0, 3));
3216        assert!(window_is_consistent(&[0.0, 0.2, -0.1, 0.3], 0.15, 0, 3));
3217        assert!(!window_is_consistent(&[0.0, 0.2, -0.2, 0.3], 0.15, 0, 3));
3218        assert!(!window_is_consistent(&[-0.2, -0.3, -0.1], 0.15, 0, 2));
3219    }
3220
3221    #[test]
3222    fn episode_ranges_are_computed_deterministically() {
3223        assert_eq!(
3224            episode_ranges(&[false, true, true, false, true, false]),
3225            vec![(1, 2), (4, 4)]
3226        );
3227    }
3228
3229    #[test]
3230    fn bounded_dsa_grid_matches_requested_size() {
3231        let grid = DsaCalibrationGrid::bounded_default();
3232        assert_eq!(grid.grid_point_count(), 81);
3233    }
3234
3235    #[test]
3236    fn ewma_normalization_is_clipped() {
3237        assert_eq!(normalize_to_threshold(5.0, 2.0), 1.0);
3238        assert_eq!(normalize_to_threshold(1.0, 2.0), 0.5);
3239        assert_eq!(normalize_to_threshold(1.0, 0.0), 0.0);
3240    }
3241
3242    #[test]
3243    fn closest_to_success_prefers_small_recall_shortfall_over_zero_recall() {
3244        let summary = summarize_dsa_grid(&[
3245            DsaCalibrationRow {
3246                config_id: 0,
3247                primary_run_signal: "feature_count_dsa_alert(k) >= 2".into(),
3248                window: 5,
3249                persistence_runs: 2,
3250                alert_tau: 2.0,
3251                corroborating_feature_count_min: 2,
3252                failure_run_recall: 102,
3253                failure_runs: 104,
3254                threshold_failure_run_recall: 104,
3255                ewma_failure_run_recall: 104,
3256                failure_recall_delta_vs_threshold: -2,
3257                failure_recall_delta_vs_ewma: -2,
3258                mean_lead_time_runs: Some(19.2),
3259                median_lead_time_runs: Some(20.0),
3260                pass_run_nuisance_proxy: 0.94,
3261                mean_lead_delta_vs_cusum_runs: Some(-0.7),
3262                mean_lead_delta_vs_run_energy_runs: Some(2.8),
3263                mean_lead_delta_vs_pca_fdc_runs: Some(0.1),
3264                mean_lead_delta_vs_threshold_runs: Some(-0.6),
3265                mean_lead_delta_vs_ewma_runs: Some(-0.6),
3266                pass_run_nuisance_delta_vs_cusum: -0.06,
3267                pass_run_nuisance_delta_vs_run_energy: 0.41,
3268                pass_run_nuisance_delta_vs_pca_fdc: 0.01,
3269                pass_run_nuisance_delta_vs_threshold: -0.03,
3270                pass_run_nuisance_delta_vs_ewma: -0.04,
3271                pass_run_nuisance_delta_vs_raw_boundary: -0.05,
3272                raw_boundary_episode_count: 100,
3273                dsa_episode_count: 10,
3274                dsa_episodes_preceding_failure: 10,
3275                mean_dsa_episode_length_runs: Some(5.0),
3276                max_dsa_episode_length_runs: 8,
3277                compression_ratio: Some(10.0),
3278                precursor_quality: Some(1.0),
3279                non_escalating_dsa_episode_fraction: Some(0.0),
3280                nuisance_improved: true,
3281                lead_time_improved: false,
3282                recall_preserved: false,
3283                compression_improved: true,
3284                any_metric_improved: true,
3285                nothing_improved: false,
3286                threshold_recall_gate_passed: false,
3287                boundary_nuisance_gate_passed: true,
3288                primary_success_condition_met: true,
3289                validation_passed: false,
3290                success_condition_failures: String::new(),
3291                validation_failures: "recall shortfall".into(),
3292            },
3293            DsaCalibrationRow {
3294                config_id: 1,
3295                primary_run_signal: "feature_count_dsa_alert(k) >= 5".into(),
3296                window: 15,
3297                persistence_runs: 4,
3298                alert_tau: 3.0,
3299                corroborating_feature_count_min: 5,
3300                failure_run_recall: 0,
3301                failure_runs: 104,
3302                threshold_failure_run_recall: 104,
3303                ewma_failure_run_recall: 104,
3304                failure_recall_delta_vs_threshold: -104,
3305                failure_recall_delta_vs_ewma: -104,
3306                mean_lead_time_runs: None,
3307                median_lead_time_runs: None,
3308                pass_run_nuisance_proxy: 0.0,
3309                mean_lead_delta_vs_cusum_runs: None,
3310                mean_lead_delta_vs_run_energy_runs: None,
3311                mean_lead_delta_vs_pca_fdc_runs: None,
3312                mean_lead_delta_vs_threshold_runs: None,
3313                mean_lead_delta_vs_ewma_runs: None,
3314                pass_run_nuisance_delta_vs_cusum: -1.0,
3315                pass_run_nuisance_delta_vs_run_energy: -0.5,
3316                pass_run_nuisance_delta_vs_pca_fdc: -0.9,
3317                pass_run_nuisance_delta_vs_threshold: -0.97,
3318                pass_run_nuisance_delta_vs_ewma: -0.98,
3319                pass_run_nuisance_delta_vs_raw_boundary: -0.99,
3320                raw_boundary_episode_count: 100,
3321                dsa_episode_count: 0,
3322                dsa_episodes_preceding_failure: 0,
3323                mean_dsa_episode_length_runs: None,
3324                max_dsa_episode_length_runs: 0,
3325                compression_ratio: None,
3326                precursor_quality: None,
3327                non_escalating_dsa_episode_fraction: None,
3328                nuisance_improved: true,
3329                lead_time_improved: false,
3330                recall_preserved: false,
3331                compression_improved: false,
3332                any_metric_improved: true,
3333                nothing_improved: false,
3334                threshold_recall_gate_passed: false,
3335                boundary_nuisance_gate_passed: true,
3336                primary_success_condition_met: false,
3337                validation_passed: false,
3338                success_condition_failures: "recall shortfall".into(),
3339                validation_failures: "recall shortfall".into(),
3340            },
3341        ]);
3342
3343        assert_eq!(summary.closest_to_success.unwrap().config_id, 0);
3344    }
3345
3346    #[test]
3347    fn policy_suppression_is_triggered_for_fragmented_recurrent_hits() {
3348        let policy = heuristic_policy_definition("recurrent_boundary_approach").unwrap();
3349        let flags = vec![
3350            true, false, false, true, false, false, false, false, false, true,
3351        ];
3352        let contribution =
3353            motif_contribution_state(policy, None, &flags, 9, true, true, true, false).unwrap();
3354
3355        assert_eq!(contribution.contribution_state, DsaPolicyState::Silent);
3356        assert!(contribution.suppressed_to_silent);
3357        assert!(contribution.fragmentation_proxy > policy.maximum_allowed_fragmentation());
3358    }
3359
3360    #[test]
3361    fn silent_motif_promotes_to_escalate_under_numeric_and_violation_corroboration() {
3362        let policy = heuristic_policy_definition("transient_excursion").unwrap();
3363        let flags = vec![false, false, true, true, true];
3364        let contribution =
3365            motif_contribution_state(policy, None, &flags, 4, true, true, true, true).unwrap();
3366
3367        assert_eq!(contribution.contribution_state, DsaPolicyState::Escalate);
3368        assert!(!contribution.suppressed_to_silent);
3369    }
3370
3371    #[test]
3372    fn corroboration_counts_only_review_or_escalate_states() {
3373        let run_count = 2;
3374        let config = DsaConfig {
3375            corroborating_feature_count_min: 2,
3376            ..DsaConfig::default()
3377        };
3378
3379        let mut review_trace = empty_trace(0, "S000", run_count);
3380        review_trace.dsa_alert = vec![true, false];
3381        review_trace.numeric_dsa_alert = vec![true, false];
3382        review_trace.policy_state = vec![DsaPolicyState::Review, DsaPolicyState::Silent];
3383
3384        let mut escalate_trace = empty_trace(1, "S001", run_count);
3385        escalate_trace.dsa_alert = vec![true, false];
3386        escalate_trace.numeric_dsa_alert = vec![true, false];
3387        escalate_trace.policy_state = vec![DsaPolicyState::Escalate, DsaPolicyState::Silent];
3388
3389        let mut watch_trace = empty_trace(2, "S002", run_count);
3390        watch_trace.dsa_alert = vec![false, false];
3391        watch_trace.numeric_dsa_alert = vec![true, true];
3392        watch_trace.policy_state = vec![DsaPolicyState::Watch, DsaPolicyState::Watch];
3393
3394        let signals = build_run_signals(
3395            &[review_trace, escalate_trace, watch_trace],
3396            &[false, false],
3397            &config,
3398            run_count,
3399        );
3400
3401        assert_eq!(signals.primary_run_alert, vec![true, false]);
3402        assert_eq!(signals.feature_count_dsa_alert, vec![2, 0]);
3403        assert_eq!(signals.watch_feature_count, vec![1, 1]);
3404        assert_eq!(signals.review_feature_count, vec![1, 0]);
3405        assert_eq!(signals.escalate_feature_count, vec![1, 0]);
3406        assert_eq!(signals.strict_escalate_run_alert, vec![false, false]);
3407        assert_eq!(signals.numeric_primary_run_alert, vec![true, false]);
3408        assert_eq!(signals.numeric_feature_count_dsa_alert, vec![3, 1]);
3409    }
3410
3411    #[test]
3412    fn priority_two_rescue_can_recover_inconsistent_watch_near_miss() {
3413        let override_entry = FeaturePolicyOverride {
3414            feature_index: 133,
3415            feature_name: "S134".into(),
3416            alert_class_override: None,
3417            requires_persistence_override: Some(false),
3418            requires_corroboration_override: Some(false),
3419            minimum_window_override: Some(5),
3420            minimum_hits_override: Some(4),
3421            maximum_allowed_fragmentation_override: Some(0.5),
3422            rescue_eligible: true,
3423            rescue_priority: 2,
3424            allow_watch_only: Some(false),
3425            allow_review_without_escalate: Some(true),
3426            suppress_if_isolated: Some(false),
3427            override_reason: "test".into(),
3428        };
3429        let rescue = RecallRescueConfig::default();
3430        let config = DsaConfig {
3431            alert_tau: 2.0,
3432            ..DsaConfig::default()
3433        };
3434        let resolved = vec![
3435            HeuristicAlertClass::Silent,
3436            HeuristicAlertClass::Watch,
3437            HeuristicAlertClass::Watch,
3438            HeuristicAlertClass::Watch,
3439            HeuristicAlertClass::Watch,
3440        ];
3441        let mut policy_state = vec![DsaPolicyState::Silent; 5];
3442        let mut dsa_alert = vec![false; 5];
3443        let fragmentation = vec![0.0, 0.25, 0.25, 0.25, 0.25];
3444        let dsa_score = vec![0.0, 1.3, 1.45, 1.55, 1.70];
3445        let boundary = vec![0.0, 0.2, 0.3, 0.4, 0.4];
3446        let ewma = vec![0.2, 0.5, 0.6, 0.66, 0.70];
3447        let motif = vec![0.0, 0.2, 0.3, 0.4, 0.4];
3448        let semantic_support = vec![false; 5];
3449        let consistent = vec![true, true, true, false, false];
3450
3451        let transition = apply_recall_rescue(
3452            Some(&override_entry),
3453            &rescue,
3454            &config,
3455            &resolved,
3456            &mut policy_state,
3457            &mut dsa_alert,
3458            &semantic_support,
3459            &fragmentation,
3460            &dsa_score,
3461            &boundary,
3462            &ewma,
3463            &motif,
3464            &consistent,
3465            4,
3466        );
3467
3468        assert_eq!(transition, Some("watch_to_review"));
3469        assert_eq!(policy_state[4], DsaPolicyState::Review);
3470        assert!(dsa_alert[4]);
3471    }
3472
3473    #[test]
3474    fn rescue_respects_feature_specific_fragmentation_override() {
3475        let override_entry = FeaturePolicyOverride {
3476            feature_index: 274,
3477            feature_name: "S275".into(),
3478            alert_class_override: None,
3479            requires_persistence_override: Some(false),
3480            requires_corroboration_override: Some(false),
3481            minimum_window_override: Some(5),
3482            minimum_hits_override: Some(4),
3483            maximum_allowed_fragmentation_override: Some(1.0),
3484            rescue_eligible: true,
3485            rescue_priority: 2,
3486            allow_watch_only: Some(false),
3487            allow_review_without_escalate: Some(true),
3488            suppress_if_isolated: Some(false),
3489            override_reason: "test".into(),
3490        };
3491        let rescue = RecallRescueConfig::default();
3492        let config = DsaConfig {
3493            alert_tau: 2.0,
3494            ..DsaConfig::default()
3495        };
3496        let resolved = vec![
3497            HeuristicAlertClass::Watch,
3498            HeuristicAlertClass::Watch,
3499            HeuristicAlertClass::Watch,
3500            HeuristicAlertClass::Watch,
3501            HeuristicAlertClass::Watch,
3502        ];
3503        let mut policy_state = vec![DsaPolicyState::Silent; 5];
3504        let mut dsa_alert = vec![false; 5];
3505        let fragmentation = vec![1.0; 5];
3506        let dsa_score = vec![1.65, 1.70, 1.74, 1.79, 1.83];
3507        let boundary = vec![0.4, 0.4, 0.4, 0.5, 0.5];
3508        let ewma = vec![0.68, 0.69, 0.70, 0.71, 0.72];
3509        let motif = vec![0.4, 0.4, 0.4, 0.5, 0.5];
3510        let semantic_support = vec![false; 5];
3511        let consistent = vec![true; 5];
3512
3513        let transition = apply_recall_rescue(
3514            Some(&override_entry),
3515            &rescue,
3516            &config,
3517            &resolved,
3518            &mut policy_state,
3519            &mut dsa_alert,
3520            &semantic_support,
3521            &fragmentation,
3522            &dsa_score,
3523            &boundary,
3524            &ewma,
3525            &motif,
3526            &consistent,
3527            4,
3528        );
3529
3530        assert_eq!(transition, Some("watch_to_review"));
3531        assert_eq!(policy_state[4], DsaPolicyState::Review);
3532        assert!(dsa_alert[4]);
3533    }
3534
3535    #[test]
3536    fn priority_three_semantic_support_can_recover_silent_failure_local_near_miss() {
3537        let override_entry = FeaturePolicyOverride {
3538            feature_index: 91,
3539            feature_name: "S092".into(),
3540            alert_class_override: Some(HeuristicAlertClass::Watch),
3541            requires_persistence_override: Some(false),
3542            requires_corroboration_override: Some(false),
3543            minimum_window_override: Some(2),
3544            minimum_hits_override: Some(1),
3545            maximum_allowed_fragmentation_override: Some(1.0),
3546            rescue_eligible: true,
3547            rescue_priority: 3,
3548            allow_watch_only: Some(false),
3549            allow_review_without_escalate: Some(true),
3550            suppress_if_isolated: Some(false),
3551            override_reason: "failure-local support".into(),
3552        };
3553        let rescue = RecallRescueConfig::default();
3554        let config = DsaConfig {
3555            alert_tau: 2.0,
3556            ..DsaConfig::default()
3557        };
3558        let resolved = vec![HeuristicAlertClass::Silent, HeuristicAlertClass::Silent];
3559        let mut policy_state = vec![DsaPolicyState::Silent; 2];
3560        let mut dsa_alert = vec![false; 2];
3561        let semantic_support = vec![true, true];
3562        let fragmentation = vec![0.5, 0.5];
3563        let dsa_score = vec![1.0, 1.0];
3564        let boundary = vec![0.5, 0.5];
3565        let ewma = vec![1.0, 1.0];
3566        let motif = vec![0.5, 0.5];
3567        let consistent = vec![true, true];
3568
3569        let transition = apply_recall_rescue(
3570            Some(&override_entry),
3571            &rescue,
3572            &config,
3573            &resolved,
3574            &mut policy_state,
3575            &mut dsa_alert,
3576            &semantic_support,
3577            &fragmentation,
3578            &dsa_score,
3579            &boundary,
3580            &ewma,
3581            &motif,
3582            &consistent,
3583            1,
3584        );
3585
3586        assert_eq!(transition, Some("silent_to_review"));
3587        assert_eq!(policy_state[1], DsaPolicyState::Review);
3588        assert!(dsa_alert[1]);
3589    }
3590
3591    #[test]
3592    fn priority_three_semantic_support_can_substitute_for_boundary_density_on_recovery_pattern() {
3593        let override_entry = FeaturePolicyOverride {
3594            feature_index: 227,
3595            feature_name: "S228".into(),
3596            alert_class_override: Some(HeuristicAlertClass::Watch),
3597            requires_persistence_override: Some(false),
3598            requires_corroboration_override: Some(false),
3599            minimum_window_override: Some(2),
3600            minimum_hits_override: Some(1),
3601            maximum_allowed_fragmentation_override: Some(1.0),
3602            rescue_eligible: true,
3603            rescue_priority: 3,
3604            allow_watch_only: Some(false),
3605            allow_review_without_escalate: Some(true),
3606            suppress_if_isolated: Some(false),
3607            override_reason: "failure-local recovery support".into(),
3608        };
3609        let rescue = RecallRescueConfig::default();
3610        let config = DsaConfig {
3611            alert_tau: 2.0,
3612            ..DsaConfig::default()
3613        };
3614        let resolved = vec![HeuristicAlertClass::Silent, HeuristicAlertClass::Silent];
3615        let mut policy_state = vec![DsaPolicyState::Silent; 2];
3616        let mut dsa_alert = vec![false; 2];
3617        let semantic_support = vec![false, true];
3618        let fragmentation = vec![0.0, 0.0];
3619        let dsa_score = vec![0.8, 1.0];
3620        let boundary = vec![0.0, 0.0];
3621        let ewma = vec![1.0, 1.0];
3622        let motif = vec![0.0, 0.0];
3623        let consistent = vec![true, true];
3624
3625        let transition = apply_recall_rescue(
3626            Some(&override_entry),
3627            &rescue,
3628            &config,
3629            &resolved,
3630            &mut policy_state,
3631            &mut dsa_alert,
3632            &semantic_support,
3633            &fragmentation,
3634            &dsa_score,
3635            &boundary,
3636            &ewma,
3637            &motif,
3638            &consistent,
3639            1,
3640        );
3641
3642        assert_eq!(transition, Some("silent_to_review"));
3643        assert_eq!(policy_state[1], DsaPolicyState::Review);
3644        assert!(dsa_alert[1]);
3645    }
3646}