1use crate::baselines::{BaselineSet, EwmaFeatureTrace};
2use crate::cohort::{MissedFailureDiagnosticRow, OperatorBurdenContributionRow};
3use crate::grammar::{FeatureGrammarTrace, GrammarReason, GrammarSet, GrammarState};
4use crate::metrics::{BenchmarkMetrics, PerFailureRunSignal};
5use crate::precursor::{DsaEvaluation, DsaFeatureTrace, DsaPolicyState, PerFailureRunDsaSignal};
6use crate::preprocessing::PreparedDataset;
7use crate::residual::{ResidualFeatureTrace, ResidualSet};
8use crate::semiotics::{
9 DsfbMotifClass, FeatureMotifTrace, GroupDefinitionRecord, MotifSet, ScaffoldSemioticsArtifacts,
10 SemanticLayer,
11};
12use crate::signs::{FeatureSigns, SignSet};
13use serde::Serialize;
14use std::collections::{BTreeMap, BTreeSet};
15
16const MAX_FAILURE_CASE_FEATURES: usize = 5;
17const MAX_FAILURE_INDEX_ACTIVITY: usize = 3;
18const MAX_MINIMAL_HEURISTICS: usize = 20;
19
20#[derive(Debug, Clone, Copy)]
21struct FeatureRoleLockSpec {
22 feature_name: &'static str,
23 initial_role: &'static str,
24 preferred_semantic_labels: &'static [&'static str],
25 preferred_grounded_motif_types: &'static [&'static str],
26 preferred_grammar_states: &'static [&'static str],
27 revised_role: Option<&'static str>,
28}
29
30const FEATURE_ROLE_LOCK: &[FeatureRoleLockSpec] = &[
31 FeatureRoleLockSpec {
32 feature_name: "S059",
33 initial_role: "primary recurrent-boundary precursor",
34 preferred_semantic_labels: &["recurrent_boundary_approach", "pre_failure_slow_drift"],
35 preferred_grounded_motif_types: &["boundary_grazing", "slow_drift_precursor"],
36 preferred_grammar_states: &["BoundaryGrazing", "SustainedDrift"],
37 revised_role: Some("persistence-gated recurrent-boundary review feature"),
38 },
39 FeatureRoleLockSpec {
40 feature_name: "S123",
41 initial_role: "transition / instability feature",
42 preferred_semantic_labels: &["transition_excursion", "persistent_instability_cluster"],
43 preferred_grounded_motif_types: &["persistent_instability", "burst_instability"],
44 preferred_grammar_states: &["TransientViolation", "PersistentViolation"],
45 revised_role: Some("transition / instability support feature"),
46 },
47 FeatureRoleLockSpec {
48 feature_name: "S133",
49 initial_role: "candidate slow-drift precursor",
50 preferred_semantic_labels: &["pre_failure_slow_drift"],
51 preferred_grounded_motif_types: &["slow_drift_precursor"],
52 preferred_grammar_states: &["SustainedDrift"],
53 revised_role: Some("semantically ambiguous review-only candidate"),
54 },
55 FeatureRoleLockSpec {
56 feature_name: "S540",
57 initial_role: "burst-support corroborator",
58 preferred_semantic_labels: &["transition_cluster_support"],
59 preferred_grounded_motif_types: &["burst_instability"],
60 preferred_grammar_states: &["TransientViolation", "PersistentViolation"],
61 revised_role: Some("review-only burst-support corroborator"),
62 },
63 FeatureRoleLockSpec {
64 feature_name: "S128",
65 initial_role: "co-burst corroborator",
66 preferred_semantic_labels: &["transition_cluster_support"],
67 preferred_grounded_motif_types: &["burst_instability"],
68 preferred_grammar_states: &["TransientViolation", "PersistentViolation"],
69 revised_role: Some("review-only co-burst corroborator"),
70 },
71 FeatureRoleLockSpec {
72 feature_name: "S104",
73 initial_role: "watch-only sentinel",
74 preferred_semantic_labels: &["watch_only_boundary_grazing"],
75 preferred_grounded_motif_types: &["boundary_grazing", "noise_like"],
76 preferred_grammar_states: &["BoundaryGrazing"],
77 revised_role: Some("watch-only nuisance sentinel"),
78 },
79 FeatureRoleLockSpec {
80 feature_name: "S134",
81 initial_role: "recall-rescue feature",
82 preferred_semantic_labels: &[],
83 preferred_grounded_motif_types: &["recovery_pattern", "persistent_instability"],
84 preferred_grammar_states: &["BoundaryGrazing", "Recovery"],
85 revised_role: Some("bounded recall-rescue feature"),
86 },
87 FeatureRoleLockSpec {
88 feature_name: "S275",
89 initial_role: "recall-rescue feature",
90 preferred_semantic_labels: &[],
91 preferred_grounded_motif_types: &["recovery_pattern", "persistent_instability"],
92 preferred_grammar_states: &["BoundaryGrazing", "Recovery"],
93 revised_role: Some("bounded recall-rescue feature"),
94 },
95];
96
97#[derive(Debug, Clone, Serialize)]
98pub struct FailuresIndex {
99 pub total_failure_count: usize,
100 pub missed_failure_ids: Vec<usize>,
101 pub entries: Vec<FailureIndexEntry>,
102}
103
104#[derive(Debug, Clone, Serialize)]
105pub struct FailureIndexEntry {
106 pub failure_id: usize,
107 pub timestamp_or_run_index: String,
108 pub timestamp: String,
109 pub detected_by_current_dsa: bool,
110 pub detected_by_dsa: bool,
111 pub detected_by_optimized_dsa: bool,
112 pub detected_by_ewma: bool,
113 pub detected_by_threshold: bool,
114 pub detected_by_cusum_if_present: bool,
115 pub detected_by_run_energy_if_present: bool,
116 pub detected_by_pca_fdc_if_present: bool,
117 pub lead_time_dsa: Option<usize>,
118 pub lead_time: Option<usize>,
119 pub lead_time_threshold: Option<usize>,
120 pub lead_time_ewma: Option<usize>,
121 pub lead_time_cusum: Option<usize>,
122 pub lead_time_run_energy: Option<usize>,
123 pub lead_time_pca_fdc: Option<usize>,
124 pub optimized_lead_time: Option<usize>,
125 pub feature_activity_summary: Vec<FeatureActivitySummary>,
126}
127
128#[derive(Debug, Clone, Serialize)]
129pub struct FeatureActivitySummary {
130 pub feature_index: usize,
131 pub feature_name: String,
132 pub max_dsa_score: f64,
133 pub max_policy_state: String,
134 pub motif_hits: usize,
135 pub pressure_hits: usize,
136}
137
138#[derive(Debug, Clone, Serialize)]
139pub struct FailureCaseReport {
140 pub failure_id: usize,
141 pub failure_timestamp: String,
142 pub baseline_detected_by_dsa: bool,
143 pub optimized_detected_by_dsa: bool,
144 pub detected_by_ewma: bool,
145 pub detected_by_threshold: bool,
146 pub baseline_dsa_lead_runs: Option<usize>,
147 pub optimized_dsa_lead_runs: Option<usize>,
148 pub ewma_lead_runs: Option<usize>,
149 pub threshold_lead_runs: Option<usize>,
150 pub exact_miss_rule: String,
151 pub failure_stage: String,
152 pub failure_explanation: String,
153 pub why_dsfb_missed: String,
154 pub missed_reason_class: String,
155 pub ewma_detection_explanation: String,
156 pub threshold_detection_explanation: String,
157 pub why_ewma_succeeded_or_failed: String,
158 pub why_threshold_succeeded_or_failed: String,
159 pub top_contributing_features: Vec<FailureCaseFeatureDetail>,
160}
161
162#[derive(Debug, Clone, Serialize)]
163pub struct FailureCaseFeatureDetail {
164 pub feature_index: usize,
165 pub feature_name: String,
166 pub behavior_classification: String,
167 pub initial_behavior_classification: String,
168 pub ranking_score_proxy: f64,
169 pub max_dsa_score: f64,
170 pub max_policy_state: String,
171 pub motif_hypothesis: String,
172 pub initial_motif_hypothesis: String,
173 pub dominant_dsfb_motif: String,
174 pub dominant_grammar_state: String,
175 pub semantic_labels: Vec<String>,
176 pub failure_stage: String,
177 pub failure_explanation: String,
178 pub residual_trajectory: Vec<f64>,
179 pub drift_trajectory: Vec<f64>,
180 pub slew_trajectory: Vec<f64>,
181 pub motif_timeline: Vec<String>,
182 pub grammar_state_timeline: Vec<String>,
183 pub grammar_trajectory: Vec<String>,
184}
185
186#[derive(Debug, Clone, Serialize)]
187pub struct MissedFailurePriorityRow {
188 pub failure_id: usize,
189 pub timestamp: String,
190 pub exact_miss_rule: String,
191 pub top_feature_name: Option<String>,
192 pub signal_strength: f64,
193 pub feature_concentration: f64,
194 pub separation_from_noise: f64,
195 pub recoverability_estimate: f64,
196 pub priority_score: f64,
197}
198
199#[derive(Debug, Clone, Serialize)]
200pub struct FeatureToMotifRecord {
201 pub feature_index: usize,
202 pub feature_name: String,
203 pub initial_role: String,
204 pub motif_type: String,
205 pub failure_behavior_justification: String,
206 pub pass_behavior_justification: String,
207 pub justification: String,
208}
209
210#[derive(Debug, Clone, Serialize)]
211pub struct NegativeControlReport {
212 pub pass_run_count: usize,
213 pub pass_run_false_activation_count: usize,
214 pub false_activation_rate: f64,
215 pub pass_run_false_episode_count: usize,
216 pub false_episode_rate: f64,
217 pub review_escalate_points_on_pass_runs: usize,
218 pub review_escalate_episodes_on_pass_runs: usize,
219 pub clean_window_count: usize,
220 pub clean_window_false_activation_count: usize,
221 pub clean_window_false_activation_rate: f64,
222 pub clean_window_false_episode_count: usize,
223 pub clean_window_false_episode_rate: f64,
224 pub review_escalate_points_on_clean_runs: usize,
225 pub review_escalate_episodes_on_clean_runs: usize,
226}
227
228#[derive(Debug, Clone, Serialize)]
229pub struct FeatureRoleValidationRow {
230 pub feature_id: String,
231 pub initial_role: String,
232 pub initial_motif: String,
233 pub supported_or_revised_or_rejected: String,
234 pub failure_behavior_summary: String,
235 pub pass_behavior_summary: String,
236 pub final_role: String,
237 pub final_motif: String,
238}
239
240#[derive(Debug, Clone, Serialize)]
241pub struct GroupValidationRow {
242 pub group_id: String,
243 pub group_members: String,
244 #[serde(rename = "failure co-activation count")]
245 pub failure_coactivation_count: usize,
246 #[serde(rename = "pass co-activation count")]
247 pub pass_coactivation_count: usize,
248 #[serde(rename = "retained_or_rejected")]
249 pub retained_or_rejected: String,
250 pub reason: String,
251}
252
253#[derive(Debug, Clone, Serialize)]
254pub struct HeuristicProvenanceRow {
255 pub heuristic_id: String,
256 pub derived_from_failures: String,
257 pub uses_features: String,
258 pub targets_nuisance_class: String,
259 pub intended_effect: String,
260 pub motif_signature: String,
261 pub allowed_grammar_states: String,
262 pub action: String,
263 pub constraints: String,
264 pub ambiguity_note: String,
265}
266
267#[derive(Debug, Clone, Serialize)]
268pub struct FeatureMotifGroundingRecord {
269 pub feature_index: usize,
270 pub feature_name: String,
271 pub motif_type: String,
272 pub dominant_dsfb_motif: String,
273 pub dominant_grammar_state: String,
274 pub failure_local_recovery_case_count: usize,
275 pub failure_window_semantic_hits: usize,
276 pub pass_run_semantic_hits: usize,
277 pub failure_window_pressure_hits: usize,
278 pub pass_run_pressure_hits: usize,
279 pub mean_abs_drift_failure: f64,
280 pub mean_abs_drift_pass: f64,
281 pub mean_abs_slew_failure: f64,
282 pub mean_abs_slew_pass: f64,
283 pub justification: String,
284}
285
286#[derive(Debug, Clone, Serialize)]
287pub struct MinimalHeuristicEntry {
288 pub heuristic_id: String,
289 pub target_problem_type: String,
290 pub target_identifier: String,
291 pub target_feature_name: Option<String>,
292 pub target_motif_type: String,
293 pub target_grammar_states: Vec<String>,
294 pub semantic_requirement: String,
295 pub policy_action: String,
296 pub burden_effect_class: String,
297 pub justification: String,
298 pub status: String,
299}
300
301#[derive(Debug, Clone, Serialize)]
302pub struct PolicyBurdenSummaryRow {
303 pub scope: String,
304 pub name: String,
305 pub watch_points: usize,
306 pub review_points: usize,
307 pub escalate_points: usize,
308 pub pass_review_escalate_points: usize,
309 pub pre_failure_review_escalate_points: usize,
310 pub silent_suppression_points: usize,
311 pub justification: String,
312}
313
314#[derive(Debug, Clone, Serialize)]
315pub struct DsfbVsEwmaCase {
316 pub failure_id: usize,
317 pub failure_timestamp: String,
318 pub recovered: bool,
319 pub recovered_feature_name: String,
320 pub baseline_miss_rule: String,
321 pub ewma_detected: bool,
322 pub ewma_lead_runs: Option<usize>,
323 pub optimized_dsa_lead_runs: Option<usize>,
324 pub explanation: String,
325 pub window: Vec<DsfbVsEwmaWindowPoint>,
326}
327
328#[derive(Debug, Clone, Serialize)]
329pub struct DsfbVsEwmaWindowPoint {
330 pub run_index: usize,
331 pub timestamp: String,
332 pub label: i8,
333 pub ewma_value: f64,
334 pub ewma_threshold: f64,
335 pub ewma_alarm: bool,
336 pub residual: f64,
337 pub drift: f64,
338 pub slew: f64,
339 pub motif_label: String,
340 pub grammar_state: String,
341 pub policy_state: String,
342}
343
344#[derive(Debug, Clone, Serialize)]
345pub struct FailureDrivenArtifacts {
346 pub failures_index: FailuresIndex,
347 pub missed_failure_priority: Vec<MissedFailurePriorityRow>,
348 pub failure_cases: Vec<FailureCaseReport>,
349 pub feature_motif_grounding: Vec<FeatureMotifGroundingRecord>,
350 pub feature_to_motif: Vec<FeatureToMotifRecord>,
351 pub negative_control_report: NegativeControlReport,
352 pub minimal_heuristics_bank: Vec<MinimalHeuristicEntry>,
353 pub heuristic_provenance: Vec<HeuristicProvenanceRow>,
354 pub policy_burden_summary: Vec<PolicyBurdenSummaryRow>,
355 pub feature_role_validation: Vec<FeatureRoleValidationRow>,
356 pub group_validation: Vec<GroupValidationRow>,
357 pub dsfb_vs_ewma_cases: Vec<DsfbVsEwmaCase>,
358}
359
360struct FeatureBundle<'a> {
361 residual: &'a ResidualFeatureTrace,
362 sign: &'a FeatureSigns,
363 grammar: &'a FeatureGrammarTrace,
364 motif: &'a FeatureMotifTrace,
365 ewma: &'a EwmaFeatureTrace,
366 baseline_dsa: &'a DsaFeatureTrace,
367 optimized_dsa: &'a DsaFeatureTrace,
368}
369
370#[derive(Debug, Clone)]
371struct FeatureActivityCandidate {
372 feature_index: usize,
373 feature_name: String,
374 ranking_score_proxy: f64,
375 max_dsa_score: f64,
376 max_policy_state: String,
377 motif_hits: usize,
378 pressure_hits: usize,
379}
380
381pub fn build_failure_driven_artifacts(
382 dataset: &PreparedDataset,
383 residuals: &ResidualSet,
384 signs: &SignSet,
385 baselines: &BaselineSet,
386 grammar: &GrammarSet,
387 motifs: &MotifSet,
388 semantic_layer: &SemanticLayer,
389 scaffold_semiotics: &ScaffoldSemioticsArtifacts,
390 metrics: &BenchmarkMetrics,
391 baseline_dsa: &DsaEvaluation,
392 optimized_dsa: &DsaEvaluation,
393 missed_failure_diagnostics: &[MissedFailureDiagnosticRow],
394 policy_operator_burden_contributions: &[OperatorBurdenContributionRow],
395 pre_failure_lookback_runs: usize,
396) -> FailureDrivenArtifacts {
397 let feature_bundles = index_feature_bundles(
398 residuals,
399 signs,
400 baselines,
401 grammar,
402 motifs,
403 baseline_dsa,
404 optimized_dsa,
405 );
406 let baseline_by_failure = baseline_dsa
407 .per_failure_run_signals
408 .iter()
409 .map(|row| (row.failure_run_index, row))
410 .collect::<BTreeMap<_, _>>();
411 let optimized_by_failure = optimized_dsa
412 .per_failure_run_signals
413 .iter()
414 .map(|row| (row.failure_run_index, row))
415 .collect::<BTreeMap<_, _>>();
416 let metrics_by_failure = metrics
417 .per_failure_run_signals
418 .iter()
419 .map(|row| (row.failure_run_index, row))
420 .collect::<BTreeMap<_, _>>();
421 let diagnostics_by_failure = missed_failure_diagnostics
422 .iter()
423 .map(|row| (row.failure_run_index, row))
424 .collect::<BTreeMap<_, _>>();
425
426 let failures_index = build_failures_index(
427 dataset,
428 &feature_bundles,
429 baseline_dsa,
430 optimized_dsa,
431 metrics,
432 pre_failure_lookback_runs,
433 );
434 let failure_cases = build_failure_cases(
435 dataset,
436 &feature_bundles,
437 semantic_layer,
438 &baseline_by_failure,
439 &optimized_by_failure,
440 &metrics_by_failure,
441 &diagnostics_by_failure,
442 pre_failure_lookback_runs,
443 );
444 let feature_motif_grounding = build_feature_motif_grounding(
445 dataset,
446 &feature_bundles,
447 semantic_layer,
448 baseline_dsa,
449 optimized_dsa,
450 missed_failure_diagnostics,
451 policy_operator_burden_contributions,
452 &failure_cases,
453 pre_failure_lookback_runs,
454 );
455 let missed_failure_priority =
456 build_missed_failure_priority(&failure_cases, &feature_motif_grounding);
457 let feature_to_motif = build_feature_to_motif(&failure_cases, &feature_motif_grounding);
458 let negative_control_report =
459 build_negative_control_report(dataset, optimized_dsa, pre_failure_lookback_runs);
460 let minimal_heuristics_bank = build_minimal_heuristics_bank(
461 missed_failure_diagnostics,
462 policy_operator_burden_contributions,
463 &feature_motif_grounding,
464 );
465 let heuristic_provenance = build_heuristic_provenance(&minimal_heuristics_bank);
466 let policy_burden_summary = build_policy_burden_summary(dataset, optimized_dsa);
467 let feature_role_validation = build_feature_role_validation(
468 dataset,
469 &feature_bundles,
470 semantic_layer,
471 &feature_motif_grounding,
472 &policy_burden_summary,
473 optimized_dsa,
474 missed_failure_diagnostics,
475 pre_failure_lookback_runs,
476 );
477 let group_validation = build_group_validation(&scaffold_semiotics.group_definitions);
478 let dsfb_vs_ewma_cases = build_dsfb_vs_ewma_cases(
479 dataset,
480 &feature_bundles,
481 &optimized_by_failure,
482 &metrics_by_failure,
483 &diagnostics_by_failure,
484 missed_failure_diagnostics,
485 pre_failure_lookback_runs,
486 );
487
488 let _ = scaffold_semiotics;
489
490 FailureDrivenArtifacts {
491 failures_index,
492 missed_failure_priority,
493 failure_cases,
494 feature_motif_grounding,
495 feature_to_motif,
496 negative_control_report,
497 minimal_heuristics_bank,
498 heuristic_provenance,
499 policy_burden_summary,
500 feature_role_validation,
501 group_validation,
502 dsfb_vs_ewma_cases,
503 }
504}
505
506pub fn validated_group_definitions(
507 definitions: &[GroupDefinitionRecord],
508) -> Vec<GroupDefinitionRecord> {
509 definitions
510 .iter()
511 .filter(|row| row.validated)
512 .cloned()
513 .collect()
514}
515
516pub fn grouped_semiotics_rejected(definitions: &[GroupDefinitionRecord]) -> bool {
517 definitions.iter().all(|row| !row.validated)
518}
519
520fn index_feature_bundles<'a>(
521 residuals: &'a ResidualSet,
522 signs: &'a SignSet,
523 baselines: &'a BaselineSet,
524 grammar: &'a GrammarSet,
525 motifs: &'a MotifSet,
526 baseline_dsa: &'a DsaEvaluation,
527 optimized_dsa: &'a DsaEvaluation,
528) -> BTreeMap<usize, FeatureBundle<'a>> {
529 let residual_by_feature = residuals
530 .traces
531 .iter()
532 .map(|row| (row.feature_index, row))
533 .collect::<BTreeMap<_, _>>();
534 let sign_by_feature = signs
535 .traces
536 .iter()
537 .map(|row| (row.feature_index, row))
538 .collect::<BTreeMap<_, _>>();
539 let grammar_by_feature = grammar
540 .traces
541 .iter()
542 .map(|row| (row.feature_index, row))
543 .collect::<BTreeMap<_, _>>();
544 let motif_by_feature = motifs
545 .traces
546 .iter()
547 .map(|row| (row.feature_index, row))
548 .collect::<BTreeMap<_, _>>();
549 let ewma_by_feature = baselines
550 .ewma
551 .iter()
552 .map(|row| (row.feature_index, row))
553 .collect::<BTreeMap<_, _>>();
554 let baseline_dsa_by_feature = baseline_dsa
555 .traces
556 .iter()
557 .map(|row| (row.feature_index, row))
558 .collect::<BTreeMap<_, _>>();
559 let optimized_dsa_by_feature = optimized_dsa
560 .traces
561 .iter()
562 .map(|row| (row.feature_index, row))
563 .collect::<BTreeMap<_, _>>();
564
565 residual_by_feature
566 .iter()
567 .filter_map(|(&feature_index, residual)| {
568 Some((
569 feature_index,
570 FeatureBundle {
571 residual,
572 sign: sign_by_feature.get(&feature_index)?,
573 grammar: grammar_by_feature.get(&feature_index)?,
574 motif: motif_by_feature.get(&feature_index)?,
575 ewma: ewma_by_feature.get(&feature_index)?,
576 baseline_dsa: baseline_dsa_by_feature.get(&feature_index)?,
577 optimized_dsa: optimized_dsa_by_feature.get(&feature_index)?,
578 },
579 ))
580 })
581 .collect()
582}
583
584fn build_failures_index(
585 dataset: &PreparedDataset,
586 feature_bundles: &BTreeMap<usize, FeatureBundle<'_>>,
587 baseline_dsa: &DsaEvaluation,
588 optimized_dsa: &DsaEvaluation,
589 metrics: &BenchmarkMetrics,
590 pre_failure_lookback_runs: usize,
591) -> FailuresIndex {
592 let optimized_by_failure = optimized_dsa
593 .per_failure_run_signals
594 .iter()
595 .map(|row| (row.failure_run_index, row))
596 .collect::<BTreeMap<_, _>>();
597 let metrics_by_failure = metrics
598 .per_failure_run_signals
599 .iter()
600 .map(|row| (row.failure_run_index, row))
601 .collect::<BTreeMap<_, _>>();
602
603 let missed_failure_ids = baseline_dsa
604 .per_failure_run_signals
605 .iter()
606 .filter(|row| row.earliest_dsa_run.is_none())
607 .map(|row| row.failure_run_index)
608 .collect::<Vec<_>>();
609
610 let entries = baseline_dsa
611 .per_failure_run_signals
612 .iter()
613 .map(|row| {
614 let optimized = optimized_by_failure
615 .get(&row.failure_run_index)
616 .copied()
617 .unwrap_or(row);
618 let metric_row = metrics_by_failure
619 .get(&row.failure_run_index)
620 .copied()
621 .unwrap_or_else(|| {
622 panic!(
623 "missing metrics failure row for failure {}",
624 row.failure_run_index
625 )
626 });
627 let activity = top_feature_activity_for_failure(
628 feature_bundles,
629 row.failure_run_index,
630 pre_failure_lookback_runs,
631 MAX_FAILURE_INDEX_ACTIVITY,
632 true,
633 )
634 .into_iter()
635 .map(|candidate| FeatureActivitySummary {
636 feature_index: candidate.feature_index,
637 feature_name: candidate.feature_name,
638 max_dsa_score: candidate.max_dsa_score,
639 max_policy_state: candidate.max_policy_state,
640 motif_hits: candidate.motif_hits,
641 pressure_hits: candidate.pressure_hits,
642 })
643 .collect::<Vec<_>>();
644
645 FailureIndexEntry {
646 failure_id: row.failure_run_index,
647 timestamp_or_run_index: row.failure_timestamp.clone(),
648 timestamp: row.failure_timestamp.clone(),
649 detected_by_current_dsa: row.earliest_dsa_run.is_some(),
650 detected_by_dsa: row.earliest_dsa_run.is_some(),
651 detected_by_optimized_dsa: optimized.earliest_dsa_run.is_some(),
652 detected_by_ewma: metric_row.earliest_ewma_run.is_some(),
653 detected_by_threshold: metric_row.earliest_threshold_run.is_some(),
654 detected_by_cusum_if_present: metric_row.earliest_cusum_run.is_some(),
655 detected_by_run_energy_if_present: metric_row.earliest_run_energy_run.is_some(),
656 detected_by_pca_fdc_if_present: metric_row.earliest_pca_fdc_run.is_some(),
657 lead_time_dsa: row.dsa_lead_runs,
658 lead_time: row.dsa_lead_runs,
659 lead_time_threshold: metric_row.threshold_lead_runs,
660 lead_time_ewma: metric_row.ewma_lead_runs,
661 lead_time_cusum: metric_row.cusum_lead_runs,
662 lead_time_run_energy: metric_row.run_energy_lead_runs,
663 lead_time_pca_fdc: metric_row.pca_fdc_lead_runs,
664 optimized_lead_time: optimized.dsa_lead_runs,
665 feature_activity_summary: activity,
666 }
667 })
668 .collect::<Vec<_>>();
669
670 let _ = dataset;
671
672 FailuresIndex {
673 total_failure_count: baseline_dsa.per_failure_run_signals.len(),
674 missed_failure_ids,
675 entries,
676 }
677}
678
679fn build_failure_cases(
680 _dataset: &PreparedDataset,
681 feature_bundles: &BTreeMap<usize, FeatureBundle<'_>>,
682 semantic_layer: &SemanticLayer,
683 baseline_by_failure: &BTreeMap<usize, &PerFailureRunDsaSignal>,
684 optimized_by_failure: &BTreeMap<usize, &PerFailureRunDsaSignal>,
685 metrics_by_failure: &BTreeMap<usize, &PerFailureRunSignal>,
686 diagnostics_by_failure: &BTreeMap<usize, &MissedFailureDiagnosticRow>,
687 pre_failure_lookback_runs: usize,
688) -> Vec<FailureCaseReport> {
689 baseline_by_failure
690 .values()
691 .filter(|row| row.earliest_dsa_run.is_none())
692 .map(|baseline_row| {
693 let failure_index = baseline_row.failure_run_index;
694 let optimized_row = optimized_by_failure
695 .get(&failure_index)
696 .copied()
697 .unwrap_or(baseline_row);
698 let metrics_row = metrics_by_failure
699 .get(&failure_index)
700 .copied()
701 .unwrap_or_else(|| panic!("missing metrics case for failure {failure_index}"));
702 let diagnostic = diagnostics_by_failure.get(&failure_index).copied();
703 let start = failure_index.saturating_sub(pre_failure_lookback_runs);
704 let top_candidates = top_feature_activity_for_failure(
705 feature_bundles,
706 failure_index,
707 pre_failure_lookback_runs,
708 MAX_FAILURE_CASE_FEATURES,
709 false,
710 );
711
712 let top_contributing_features = top_candidates
713 .into_iter()
714 .map(|candidate| {
715 let bundle = feature_bundles
716 .get(&candidate.feature_index)
717 .unwrap_or_else(|| {
718 panic!("missing feature bundle {}", candidate.feature_index)
719 });
720 let semantic_labels = semantic_labels_in_window(
721 semantic_layer,
722 &candidate.feature_name,
723 start,
724 failure_index,
725 );
726 let dominant_dsfb_motif =
727 dominant_motif_in_window(bundle.motif, start, failure_index);
728 let dominant_grammar_state =
729 dominant_grammar_state_in_window(bundle.grammar, start, failure_index);
730 let initial_motif_hypothesis =
731 grounded_motif_type_for_window(bundle, start, failure_index).to_string();
732 let behavior_classification = classify_feature_behavior(
733 &bundle.sign.drift[start..failure_index],
734 &bundle.sign.slew[start..failure_index],
735 )
736 .to_string();
737 let (failure_stage, failure_explanation) = feature_failure_explanation(
738 bundle,
739 start,
740 failure_index,
741 diagnostic,
742 &semantic_labels,
743 );
744
745 FailureCaseFeatureDetail {
746 feature_index: candidate.feature_index,
747 feature_name: candidate.feature_name.clone(),
748 behavior_classification: behavior_classification.clone(),
749 initial_behavior_classification: behavior_classification,
750 ranking_score_proxy: candidate.ranking_score_proxy,
751 max_dsa_score: candidate.max_dsa_score,
752 max_policy_state: candidate.max_policy_state,
753 motif_hypothesis: initial_motif_hypothesis.clone(),
754 initial_motif_hypothesis,
755 dominant_dsfb_motif,
756 dominant_grammar_state,
757 semantic_labels,
758 failure_stage,
759 failure_explanation,
760 residual_trajectory: bundle.residual.residuals[start..failure_index]
761 .to_vec(),
762 drift_trajectory: bundle.sign.drift[start..failure_index].to_vec(),
763 slew_trajectory: bundle.sign.slew[start..failure_index].to_vec(),
764 motif_timeline: bundle.motif.labels[start..failure_index]
765 .iter()
766 .map(|label| label.as_lowercase().to_string())
767 .collect(),
768 grammar_state_timeline: (start..failure_index)
769 .map(|run_index| {
770 failure_grammar_state_label(bundle.grammar, run_index).to_string()
771 })
772 .collect(),
773 grammar_trajectory: (start..failure_index)
774 .map(|run_index| {
775 failure_grammar_state_label(bundle.grammar, run_index).to_string()
776 })
777 .collect(),
778 }
779 })
780 .collect::<Vec<_>>();
781
782 let threshold_support = scalar_supporting_features(
783 feature_bundles,
784 start,
785 failure_index,
786 ScalarSupportMode::Threshold,
787 );
788 let ewma_support = scalar_supporting_features(
789 feature_bundles,
790 start,
791 failure_index,
792 ScalarSupportMode::Ewma,
793 );
794
795 FailureCaseReport {
796 failure_id: failure_index,
797 failure_timestamp: baseline_row.failure_timestamp.clone(),
798 baseline_detected_by_dsa: baseline_row.earliest_dsa_run.is_some(),
799 optimized_detected_by_dsa: optimized_row.earliest_dsa_run.is_some(),
800 detected_by_ewma: metrics_row.earliest_ewma_run.is_some(),
801 detected_by_threshold: metrics_row.earliest_threshold_run.is_some(),
802 baseline_dsa_lead_runs: baseline_row.dsa_lead_runs,
803 optimized_dsa_lead_runs: optimized_row.dsa_lead_runs,
804 ewma_lead_runs: metrics_row.ewma_lead_runs,
805 threshold_lead_runs: metrics_row.threshold_lead_runs,
806 exact_miss_rule: diagnostic
807 .map(|row| row.exact_miss_rule.clone())
808 .unwrap_or_else(|| "unclassified".into()),
809 failure_stage: failure_stage_label(diagnostic),
810 failure_explanation: failure_explanation_text(diagnostic, optimized_row),
811 ewma_detection_explanation: scalar_detection_explanation(
812 "EWMA",
813 metrics_row.earliest_ewma_run.is_some(),
814 metrics_row.ewma_lead_runs,
815 &ewma_support,
816 ),
817 threshold_detection_explanation: scalar_detection_explanation(
818 "Threshold",
819 metrics_row.earliest_threshold_run.is_some(),
820 metrics_row.threshold_lead_runs,
821 &threshold_support,
822 ),
823 why_dsfb_missed: failure_explanation_text(diagnostic, optimized_row),
824 missed_reason_class: miss_reason_class(diagnostic, optimized_row).into(),
825 why_ewma_succeeded_or_failed: scalar_detection_explanation(
826 "EWMA",
827 metrics_row.earliest_ewma_run.is_some(),
828 metrics_row.ewma_lead_runs,
829 &ewma_support,
830 ),
831 why_threshold_succeeded_or_failed: scalar_detection_explanation(
832 "Threshold",
833 metrics_row.earliest_threshold_run.is_some(),
834 metrics_row.threshold_lead_runs,
835 &threshold_support,
836 ),
837 top_contributing_features,
838 }
839 })
840 .collect()
841}
842
843fn build_feature_motif_grounding(
844 dataset: &PreparedDataset,
845 feature_bundles: &BTreeMap<usize, FeatureBundle<'_>>,
846 semantic_layer: &SemanticLayer,
847 baseline_dsa: &DsaEvaluation,
848 optimized_dsa: &DsaEvaluation,
849 missed_failure_diagnostics: &[MissedFailureDiagnosticRow],
850 policy_operator_burden_contributions: &[OperatorBurdenContributionRow],
851 failure_cases: &[FailureCaseReport],
852 pre_failure_lookback_runs: usize,
853) -> Vec<FeatureMotifGroundingRecord> {
854 let failure_indices = baseline_dsa
855 .per_failure_run_signals
856 .iter()
857 .map(|row| row.failure_run_index)
858 .collect::<Vec<_>>();
859 let failure_window_mask = build_failure_window_mask(
860 dataset.labels.len(),
861 &failure_indices,
862 pre_failure_lookback_runs,
863 );
864 let candidate_features = grounding_candidate_features(
865 baseline_dsa,
866 optimized_dsa,
867 missed_failure_diagnostics,
868 policy_operator_burden_contributions,
869 failure_cases,
870 );
871
872 candidate_features
873 .into_iter()
874 .filter_map(|feature_index| {
875 let bundle = feature_bundles.get(&feature_index)?;
876 let failure_semantic_hits = semantic_hit_count(
877 semantic_layer,
878 &bundle.residual.feature_name,
879 &failure_window_mask,
880 true,
881 );
882 let pass_semantic_hits = semantic_hit_count(
883 semantic_layer,
884 &bundle.residual.feature_name,
885 &failure_window_mask,
886 false,
887 );
888 let failure_pressure_hits = pressure_hit_count(bundle.grammar, &failure_window_mask, true);
889 let pass_pressure_hits = pressure_hit_count(bundle.grammar, &failure_window_mask, false);
890 let mean_abs_drift_failure =
891 masked_mean_abs(&bundle.sign.drift, &failure_window_mask, true);
892 let mean_abs_drift_pass = masked_mean_abs(&bundle.sign.drift, &failure_window_mask, false);
893 let mean_abs_slew_failure =
894 masked_mean_abs(&bundle.sign.slew, &failure_window_mask, true);
895 let mean_abs_slew_pass = masked_mean_abs(&bundle.sign.slew, &failure_window_mask, false);
896 let dominant_dsfb_motif = dominant_motif_for_mask(bundle.motif, &failure_window_mask, true);
897 let dominant_grammar_state =
898 dominant_grammar_state_for_mask(bundle.grammar, &failure_window_mask, true);
899 let failure_local_recovery_case_count = failure_cases
900 .iter()
901 .flat_map(|case| case.top_contributing_features.iter())
902 .filter(|feature| {
903 feature.feature_index == feature_index
904 && feature.initial_motif_hypothesis == "recovery_pattern"
905 && feature.dominant_grammar_state == "Recovery"
906 })
907 .count();
908 let motif_type = grounded_motif_type(
909 dominant_dsfb_motif.as_str(),
910 dominant_grammar_state.as_str(),
911 failure_local_recovery_case_count,
912 failure_semantic_hits,
913 pass_semantic_hits,
914 failure_pressure_hits,
915 pass_pressure_hits,
916 mean_abs_drift_failure,
917 mean_abs_slew_failure,
918 );
919
920 Some(FeatureMotifGroundingRecord {
921 feature_index,
922 feature_name: bundle.residual.feature_name.clone(),
923 motif_type: motif_type.to_string(),
924 dominant_dsfb_motif,
925 dominant_grammar_state,
926 failure_local_recovery_case_count,
927 failure_window_semantic_hits: failure_semantic_hits,
928 pass_run_semantic_hits: pass_semantic_hits,
929 failure_window_pressure_hits: failure_pressure_hits,
930 pass_run_pressure_hits: pass_pressure_hits,
931 mean_abs_drift_failure,
932 mean_abs_drift_pass,
933 mean_abs_slew_failure,
934 mean_abs_slew_pass,
935 justification: format!(
936 "Failure-window semantic hits={}, pass-run semantic hits={}, failure pressure hits={}, pass pressure hits={}, failure-local recovery cases={}, |drift|_failure={:.4}, |slew|_failure={:.4}.",
937 failure_semantic_hits,
938 pass_semantic_hits,
939 failure_pressure_hits,
940 pass_pressure_hits,
941 failure_local_recovery_case_count,
942 mean_abs_drift_failure,
943 mean_abs_slew_failure,
944 ),
945 })
946 })
947 .collect()
948}
949
950fn build_missed_failure_priority(
951 failure_cases: &[FailureCaseReport],
952 feature_motif_grounding: &[FeatureMotifGroundingRecord],
953) -> Vec<MissedFailurePriorityRow> {
954 let grounding_by_name = feature_motif_grounding
955 .iter()
956 .map(|row| (row.feature_name.as_str(), row))
957 .collect::<BTreeMap<_, _>>();
958 let mut rows = failure_cases
959 .iter()
960 .map(|case| {
961 let signal_strength = case
962 .top_contributing_features
963 .iter()
964 .map(|feature| feature.max_dsa_score)
965 .fold(0.0, f64::max);
966 let total_proxy = case
967 .top_contributing_features
968 .iter()
969 .map(|feature| feature.ranking_score_proxy)
970 .sum::<f64>();
971 let feature_concentration = case
972 .top_contributing_features
973 .first()
974 .map(|feature| feature.ranking_score_proxy / total_proxy.max(1.0e-9))
975 .unwrap_or(0.0);
976 let top_feature_name = case
977 .top_contributing_features
978 .first()
979 .map(|feature| feature.feature_name.clone());
980 let separation_from_noise = top_feature_name
981 .as_deref()
982 .and_then(|feature_name| grounding_by_name.get(feature_name))
983 .map(|row| {
984 (row.failure_window_semantic_hits as f64
985 + row.failure_window_pressure_hits as f64)
986 / (1.0
987 + row.pass_run_semantic_hits as f64
988 + row.pass_run_pressure_hits as f64)
989 })
990 .unwrap_or(0.0);
991 let recoverability_estimate = if case.optimized_detected_by_dsa {
992 1.0
993 } else {
994 0.0
995 };
996 let priority_score = signal_strength
997 + feature_concentration
998 + separation_from_noise
999 + recoverability_estimate;
1000 MissedFailurePriorityRow {
1001 failure_id: case.failure_id,
1002 timestamp: case.failure_timestamp.clone(),
1003 exact_miss_rule: case.exact_miss_rule.clone(),
1004 top_feature_name,
1005 signal_strength,
1006 feature_concentration,
1007 separation_from_noise,
1008 recoverability_estimate,
1009 priority_score,
1010 }
1011 })
1012 .collect::<Vec<_>>();
1013 rows.sort_by(|left, right| {
1014 right
1015 .priority_score
1016 .partial_cmp(&left.priority_score)
1017 .unwrap_or(std::cmp::Ordering::Equal)
1018 .then_with(|| left.failure_id.cmp(&right.failure_id))
1019 });
1020 rows
1021}
1022
1023fn build_feature_to_motif(
1024 failure_cases: &[FailureCaseReport],
1025 feature_motif_grounding: &[FeatureMotifGroundingRecord],
1026) -> Vec<FeatureToMotifRecord> {
1027 let mut counts = BTreeMap::<usize, usize>::new();
1028 for case in failure_cases {
1029 for feature in &case.top_contributing_features {
1030 *counts.entry(feature.feature_index).or_default() += 1;
1031 }
1032 }
1033 let grounding_by_index = feature_motif_grounding
1034 .iter()
1035 .map(|row| (row.feature_index, row))
1036 .collect::<BTreeMap<_, _>>();
1037 let mut selected = counts.into_iter().collect::<Vec<_>>();
1038 selected.sort_by(|left, right| right.1.cmp(&left.1).then_with(|| left.0.cmp(&right.0)));
1039 let mut ordered_feature_indices = Vec::new();
1040 let mut seen = BTreeSet::new();
1041
1042 for spec in FEATURE_ROLE_LOCK {
1043 if let Some(feature_index) = feature_index_from_name(spec.feature_name) {
1044 if seen.insert(feature_index) {
1045 ordered_feature_indices.push(feature_index);
1046 }
1047 }
1048 }
1049 for (feature_index, _) in selected {
1050 if ordered_feature_indices.len() >= 15 {
1051 break;
1052 }
1053 if seen.insert(feature_index) {
1054 ordered_feature_indices.push(feature_index);
1055 }
1056 }
1057
1058 ordered_feature_indices
1059 .into_iter()
1060 .filter_map(|feature_index| {
1061 let grounding = grounding_by_index.get(&feature_index)?;
1062 let initial_role = FEATURE_ROLE_LOCK
1063 .iter()
1064 .find(|spec| spec.feature_name == grounding.feature_name)
1065 .map(|spec| spec.initial_role)
1066 .unwrap_or("failure-ranked candidate feature");
1067 Some(FeatureToMotifRecord {
1068 feature_index,
1069 feature_name: grounding.feature_name.clone(),
1070 initial_role: initial_role.into(),
1071 motif_type: grounding.motif_type.clone(),
1072 failure_behavior_justification: format!(
1073 "failure semantic hits={}, failure pressure hits={}, dominant failure motif={}, dominant failure grammar={}",
1074 grounding.failure_window_semantic_hits,
1075 grounding.failure_window_pressure_hits,
1076 grounding.dominant_dsfb_motif,
1077 grounding.dominant_grammar_state,
1078 ),
1079 pass_behavior_justification: format!(
1080 "pass semantic hits={}, pass pressure hits={}, |drift|_pass={:.4}, |slew|_pass={:.4}",
1081 grounding.pass_run_semantic_hits,
1082 grounding.pass_run_pressure_hits,
1083 grounding.mean_abs_drift_pass,
1084 grounding.mean_abs_slew_pass,
1085 ),
1086 justification: grounding.justification.clone(),
1087 })
1088 })
1089 .collect()
1090}
1091
1092fn build_negative_control_report(
1093 dataset: &PreparedDataset,
1094 optimized_dsa: &DsaEvaluation,
1095 pre_failure_lookback_runs: usize,
1096) -> NegativeControlReport {
1097 let failure_indices = dataset
1098 .labels
1099 .iter()
1100 .enumerate()
1101 .filter_map(|(run_index, label)| (*label == 1).then_some(run_index))
1102 .collect::<Vec<_>>();
1103 let failure_window_mask = build_failure_window_mask(
1104 dataset.labels.len(),
1105 &failure_indices,
1106 pre_failure_lookback_runs,
1107 );
1108 let pass_run_count = dataset.labels.iter().filter(|label| **label == -1).count();
1109 let pass_run_false_activation_count = optimized_dsa
1110 .run_signals
1111 .primary_run_alert
1112 .iter()
1113 .enumerate()
1114 .filter(|(run_index, flag)| dataset.labels[*run_index] == -1 && **flag)
1115 .count();
1116 let pass_only_signal = optimized_dsa
1117 .run_signals
1118 .primary_run_alert
1119 .iter()
1120 .enumerate()
1121 .map(|(run_index, flag)| dataset.labels[run_index] == -1 && *flag)
1122 .collect::<Vec<_>>();
1123 let clean_window_mask = dataset
1124 .labels
1125 .iter()
1126 .enumerate()
1127 .map(|(run_index, label)| *label == -1 && !failure_window_mask[run_index])
1128 .collect::<Vec<_>>();
1129 let clean_window_count = clean_window_mask.iter().filter(|flag| **flag).count();
1130 let clean_window_false_activation_count = optimized_dsa
1131 .run_signals
1132 .primary_run_alert
1133 .iter()
1134 .enumerate()
1135 .filter(|(run_index, flag)| clean_window_mask[*run_index] && **flag)
1136 .count();
1137 let clean_window_signal = optimized_dsa
1138 .run_signals
1139 .primary_run_alert
1140 .iter()
1141 .enumerate()
1142 .map(|(run_index, flag)| clean_window_mask[run_index] && *flag)
1143 .collect::<Vec<_>>();
1144
1145 NegativeControlReport {
1146 pass_run_count,
1147 pass_run_false_activation_count,
1148 false_activation_rate: pass_run_false_activation_count as f64
1149 / pass_run_count.max(1) as f64,
1150 pass_run_false_episode_count: episode_ranges(&pass_only_signal).len(),
1151 false_episode_rate: episode_ranges(&pass_only_signal).len() as f64
1152 / pass_run_count.max(1) as f64,
1153 review_escalate_points_on_pass_runs: pass_run_false_activation_count,
1154 review_escalate_episodes_on_pass_runs: episode_ranges(&pass_only_signal).len(),
1155 clean_window_count,
1156 clean_window_false_activation_count,
1157 clean_window_false_activation_rate: clean_window_false_activation_count as f64
1158 / clean_window_count.max(1) as f64,
1159 clean_window_false_episode_count: episode_ranges(&clean_window_signal).len(),
1160 clean_window_false_episode_rate: episode_ranges(&clean_window_signal).len() as f64
1161 / clean_window_count.max(1) as f64,
1162 review_escalate_points_on_clean_runs: clean_window_false_activation_count,
1163 review_escalate_episodes_on_clean_runs: episode_ranges(&clean_window_signal).len(),
1164 }
1165}
1166
1167fn build_heuristic_provenance(
1168 minimal_heuristics_bank: &[MinimalHeuristicEntry],
1169) -> Vec<HeuristicProvenanceRow> {
1170 minimal_heuristics_bank
1171 .iter()
1172 .map(|entry| {
1173 let derived_from_failures = if entry.target_problem_type == "missed_failure" {
1174 bracketed_csv_list(&[entry.target_identifier.clone()])
1175 } else {
1176 "[]".into()
1177 };
1178 let uses_features = entry
1179 .target_feature_name
1180 .as_ref()
1181 .map(|feature_name| bracketed_csv_list(std::slice::from_ref(feature_name)))
1182 .unwrap_or_else(|| "[]".into());
1183 let targets_nuisance_class = if entry.target_problem_type == "nuisance_class" {
1184 entry.target_identifier.clone()
1185 } else {
1186 String::new()
1187 };
1188 let intended_effect = if entry.target_problem_type == "missed_failure" {
1189 "recover_failure"
1190 } else {
1191 "suppress_burden"
1192 };
1193 HeuristicProvenanceRow {
1194 heuristic_id: entry.heuristic_id.clone(),
1195 derived_from_failures,
1196 uses_features,
1197 targets_nuisance_class,
1198 intended_effect: intended_effect.into(),
1199 motif_signature: entry.target_motif_type.clone(),
1200 allowed_grammar_states: entry.target_grammar_states.join("|"),
1201 action: entry.policy_action.clone(),
1202 constraints: format!(
1203 "semantic_requirement={}, status={}, burden_effect_class={}",
1204 entry.semantic_requirement, entry.status, entry.burden_effect_class
1205 ),
1206 ambiguity_note:
1207 "heuristic remains bounded to one failure or nuisance class and does not imply mechanism identity"
1208 .into(),
1209 }
1210 })
1211 .collect()
1212}
1213
1214fn build_feature_role_validation(
1215 dataset: &PreparedDataset,
1216 feature_bundles: &BTreeMap<usize, FeatureBundle<'_>>,
1217 semantic_layer: &SemanticLayer,
1218 feature_motif_grounding: &[FeatureMotifGroundingRecord],
1219 policy_burden_summary: &[PolicyBurdenSummaryRow],
1220 optimized_dsa: &DsaEvaluation,
1221 missed_failure_diagnostics: &[MissedFailureDiagnosticRow],
1222 pre_failure_lookback_runs: usize,
1223) -> Vec<FeatureRoleValidationRow> {
1224 let failure_indices = optimized_dsa
1225 .per_failure_run_signals
1226 .iter()
1227 .map(|row| row.failure_run_index)
1228 .collect::<Vec<_>>();
1229 let failure_window_mask = build_failure_window_mask(
1230 dataset.labels.len(),
1231 &failure_indices,
1232 pre_failure_lookback_runs,
1233 );
1234 let grounding_by_name = feature_motif_grounding
1235 .iter()
1236 .map(|row| (row.feature_name.as_str(), row))
1237 .collect::<BTreeMap<_, _>>();
1238 let burden_by_name = policy_burden_summary
1239 .iter()
1240 .filter(|row| row.scope == "feature")
1241 .map(|row| (row.name.as_str(), row))
1242 .collect::<BTreeMap<_, _>>();
1243 let earliest_counts = optimized_dsa
1244 .per_failure_run_signals
1245 .iter()
1246 .filter_map(|row| row.earliest_dsa_feature_name.as_deref())
1247 .fold(BTreeMap::<&str, usize>::new(), |mut acc, feature_name| {
1248 *acc.entry(feature_name).or_default() += 1;
1249 acc
1250 });
1251 let max_score_counts = optimized_dsa
1252 .per_failure_run_signals
1253 .iter()
1254 .filter_map(|row| row.max_dsa_score_feature_name.as_deref())
1255 .fold(BTreeMap::<&str, usize>::new(), |mut acc, feature_name| {
1256 *acc.entry(feature_name).or_default() += 1;
1257 acc
1258 });
1259 let direct_miss_counts = missed_failure_diagnostics
1260 .iter()
1261 .filter_map(|row| row.nearest_feature_name.as_deref())
1262 .fold(BTreeMap::<&str, usize>::new(), |mut acc, feature_name| {
1263 *acc.entry(feature_name).or_default() += 1;
1264 acc
1265 });
1266 let optimized_target_counts = missed_failure_diagnostics
1267 .iter()
1268 .filter_map(|row| row.optimized_feature_name.as_deref())
1269 .fold(BTreeMap::<&str, usize>::new(), |mut acc, feature_name| {
1270 *acc.entry(feature_name).or_default() += 1;
1271 acc
1272 });
1273 let recovered_by_name = missed_failure_diagnostics
1274 .iter()
1275 .filter(|row| row.recovered_after_optimization)
1276 .filter_map(|row| row.optimized_feature_name.as_deref())
1277 .fold(BTreeMap::<&str, usize>::new(), |mut acc, feature_name| {
1278 *acc.entry(feature_name).or_default() += 1;
1279 acc
1280 });
1281
1282 FEATURE_ROLE_LOCK
1283 .iter()
1284 .map(|spec| {
1285 let feature_name = spec.feature_name;
1286 let grounding = grounding_by_name.get(feature_name).copied();
1287 let burden = burden_by_name.get(feature_name).copied();
1288 let bundle = feature_index_from_name(feature_name)
1289 .and_then(|feature_index| feature_bundles.get(&feature_index));
1290 let earliest_count = earliest_counts.get(feature_name).copied().unwrap_or(0);
1291 let max_score_count = max_score_counts.get(feature_name).copied().unwrap_or(0);
1292 let direct_miss_count = direct_miss_counts.get(feature_name).copied().unwrap_or(0);
1293 let optimized_target_count = optimized_target_counts.get(feature_name).copied().unwrap_or(0);
1294 let recovered_count = recovered_by_name.get(feature_name).copied().unwrap_or(0);
1295 let pass_burden = burden
1296 .map(|row| row.pass_review_escalate_points)
1297 .unwrap_or(0);
1298 let linked_failure_ids = linked_failure_ids_for_feature(
1299 feature_name,
1300 optimized_dsa,
1301 missed_failure_diagnostics,
1302 );
1303 let preferred_failure_semantic_hits = preferred_semantic_hit_count(
1304 semantic_layer,
1305 feature_name,
1306 &failure_window_mask,
1307 true,
1308 spec.preferred_semantic_labels,
1309 );
1310 let preferred_pass_semantic_hits = preferred_semantic_hit_count(
1311 semantic_layer,
1312 feature_name,
1313 &failure_window_mask,
1314 false,
1315 spec.preferred_semantic_labels,
1316 );
1317 let preferred_failure_grammar_hits = bundle
1318 .map(|bundle| {
1319 preferred_grammar_hit_count(
1320 bundle.grammar,
1321 &failure_window_mask,
1322 true,
1323 spec.preferred_grammar_states,
1324 )
1325 })
1326 .unwrap_or(0);
1327 let preferred_pass_grammar_hits = bundle
1328 .map(|bundle| {
1329 preferred_grammar_hit_count(
1330 bundle.grammar,
1331 &failure_window_mask,
1332 false,
1333 spec.preferred_grammar_states,
1334 )
1335 })
1336 .unwrap_or(0);
1337 let (linked_groundings, preferred_grounded_windows) = bundle
1338 .map(|bundle| {
1339 linked_window_grounding_counts(
1340 bundle,
1341 &linked_failure_ids,
1342 pre_failure_lookback_runs,
1343 spec.preferred_grounded_motif_types,
1344 )
1345 })
1346 .unwrap_or_default();
1347 let (linked_grammar, preferred_grammar_windows) = bundle
1348 .map(|bundle| {
1349 linked_window_grammar_counts(
1350 bundle.grammar,
1351 &linked_failure_ids,
1352 pre_failure_lookback_runs,
1353 spec.preferred_grammar_states,
1354 )
1355 })
1356 .unwrap_or_default();
1357
1358 let structural_support = preferred_grounded_windows > 0
1359 || preferred_grammar_windows > 0
1360 || (preferred_failure_semantic_hits > 0
1361 && preferred_failure_semantic_hits >= preferred_pass_semantic_hits);
1362 let operational_failure_link = earliest_count + max_score_count + direct_miss_count + optimized_target_count;
1363 let (validation_result, final_role) = match feature_name {
1364 "S059" => {
1365 if structural_support && operational_failure_link > 0 {
1366 ("supported", spec.initial_role)
1367 } else if operational_failure_link > 0 {
1368 (
1369 "revised",
1370 spec.revised_role.unwrap_or(spec.initial_role),
1371 )
1372 } else if pass_burden > 0 {
1373 (
1374 "rejected",
1375 "high-burden recurrent-boundary candidate without failure-local support",
1376 )
1377 } else {
1378 ("revised", spec.revised_role.unwrap_or(spec.initial_role))
1379 }
1380 }
1381 "S123" => {
1382 if structural_support && (earliest_count > 0 || max_score_count > 0) {
1383 ("supported", spec.initial_role)
1384 } else if operational_failure_link > 0 {
1385 (
1386 "revised",
1387 spec.revised_role.unwrap_or(spec.initial_role),
1388 )
1389 } else {
1390 ("rejected", "transition / instability feature without failure-local support")
1391 }
1392 }
1393 "S133" => {
1394 if structural_support
1395 && preferred_failure_semantic_hits > preferred_pass_semantic_hits
1396 && preferred_grammar_windows > 0
1397 {
1398 ("supported", spec.initial_role)
1399 } else {
1400 (
1401 "revised",
1402 spec.revised_role.unwrap_or(spec.initial_role),
1403 )
1404 }
1405 }
1406 "S540" | "S128" => {
1407 if structural_support && max_score_count > 0 {
1408 ("supported", spec.initial_role)
1409 } else if operational_failure_link > 0 {
1410 (
1411 "revised",
1412 spec.revised_role.unwrap_or(spec.initial_role),
1413 )
1414 } else {
1415 ("rejected", "corroborator remained failure-unlinked")
1416 }
1417 }
1418 "S104" => {
1419 let sentinel_supported = grounding.is_some_and(|row| {
1420 matches!(row.motif_type.as_str(), "boundary_grazing" | "noise_like")
1421 }) && pass_burden == 0
1422 && earliest_count == 0
1423 && recovered_count == 0;
1424 if sentinel_supported {
1425 ("supported", spec.initial_role)
1426 } else {
1427 (
1428 "revised",
1429 spec.revised_role.unwrap_or(spec.initial_role),
1430 )
1431 }
1432 }
1433 "S134" | "S275" => {
1434 if direct_miss_count > 0 || optimized_target_count > 0 || recovered_count > 0 {
1435 (
1436 "supported",
1437 spec.revised_role.unwrap_or(spec.initial_role),
1438 )
1439 } else {
1440 ("rejected", "unlinked recall-rescue feature")
1441 }
1442 }
1443 _ => ("supported", spec.initial_role),
1444 };
1445
1446 FeatureRoleValidationRow {
1447 feature_id: feature_name.into(),
1448 initial_role: spec.initial_role.into(),
1449 initial_motif: spec
1450 .preferred_grounded_motif_types
1451 .first()
1452 .copied()
1453 .unwrap_or("null")
1454 .into(),
1455 supported_or_revised_or_rejected: validation_result.into(),
1456 failure_behavior_summary: format!(
1457 "preferred semantic hits failure/pass={}/{}, preferred grammar hits failure/pass={}/{}, linked failure windows={}, linked grounded motifs={}, linked dominant grammar={}, earliest detection count={}, max-score failure count={}, direct miss linkages={}, optimized rescue targets={}, recovered missed failures={}, linked failures={}",
1458 preferred_failure_semantic_hits,
1459 preferred_pass_semantic_hits,
1460 preferred_failure_grammar_hits,
1461 preferred_pass_grammar_hits,
1462 linked_failure_ids.len(),
1463 summarize_count_map(&linked_groundings),
1464 summarize_count_map(&linked_grammar),
1465 earliest_count,
1466 max_score_count,
1467 direct_miss_count,
1468 optimized_target_count,
1469 recovered_count,
1470 bracketed_usize_list(&linked_failure_ids),
1471 ),
1472 pass_behavior_summary: burden
1473 .map(|row| {
1474 format!(
1475 "pass review/escalate points={}, silent suppressions={}, preferred semantic pass hits={}, preferred grammar pass hits={}, dominant pass grounding={}",
1476 row.pass_review_escalate_points,
1477 row.silent_suppression_points,
1478 preferred_pass_semantic_hits,
1479 preferred_pass_grammar_hits,
1480 grounding
1481 .map(|grounding| format!(
1482 "{} via {}",
1483 grounding.motif_type, grounding.dominant_dsfb_motif
1484 ))
1485 .unwrap_or_else(|| "none".into()),
1486 )
1487 })
1488 .unwrap_or_else(|| {
1489 format!(
1490 "no pass-run burden, preferred semantic pass hits={}, preferred grammar pass hits={}, dominant pass grounding={}",
1491 preferred_pass_semantic_hits,
1492 preferred_pass_grammar_hits,
1493 grounding
1494 .map(|grounding| format!(
1495 "{} via {}",
1496 grounding.motif_type, grounding.dominant_dsfb_motif
1497 ))
1498 .unwrap_or_else(|| "none".into()),
1499 )
1500 }),
1501 final_role: final_role.into(),
1502 final_motif: grounding
1503 .map(|row| row.motif_type.clone())
1504 .unwrap_or_else(|| "null".into()),
1505 }
1506 })
1507 .collect()
1508}
1509
1510fn build_group_validation(definitions: &[GroupDefinitionRecord]) -> Vec<GroupValidationRow> {
1511 definitions
1512 .iter()
1513 .map(|row| GroupValidationRow {
1514 group_id: group_display_name(&row.group_name).into(),
1515 group_members: row.member_features.clone(),
1516 failure_coactivation_count: row.failure_coactivation_run_count,
1517 pass_coactivation_count: row.pass_coactivation_run_count,
1518 retained_or_rejected: if row.validated {
1519 "retained".into()
1520 } else {
1521 "rejected".into()
1522 },
1523 reason: row
1524 .rejection_reason
1525 .clone()
1526 .unwrap_or_else(|| "validated by empirical failure-vs-pass gate".into()),
1527 })
1528 .collect()
1529}
1530
1531fn linked_failure_ids_for_feature(
1532 feature_name: &str,
1533 optimized_dsa: &DsaEvaluation,
1534 missed_failure_diagnostics: &[MissedFailureDiagnosticRow],
1535) -> Vec<usize> {
1536 let mut linked = BTreeSet::new();
1537 for row in &optimized_dsa.per_failure_run_signals {
1538 if row.earliest_dsa_feature_name.as_deref() == Some(feature_name)
1539 || row.max_dsa_score_feature_name.as_deref() == Some(feature_name)
1540 {
1541 linked.insert(row.failure_run_index);
1542 }
1543 }
1544 for row in missed_failure_diagnostics {
1545 if row.nearest_feature_name.as_deref() == Some(feature_name)
1546 || row.optimized_feature_name.as_deref() == Some(feature_name)
1547 {
1548 linked.insert(row.failure_run_index);
1549 }
1550 }
1551 linked.into_iter().collect()
1552}
1553
1554fn preferred_semantic_hit_count(
1555 semantic_layer: &SemanticLayer,
1556 feature_name: &str,
1557 failure_window_mask: &[bool],
1558 in_failure_window: bool,
1559 preferred_labels: &[&str],
1560) -> usize {
1561 if preferred_labels.is_empty() {
1562 return 0;
1563 }
1564 semantic_layer
1565 .semantic_matches
1566 .iter()
1567 .filter(|row| row.feature_name == feature_name)
1568 .filter(|row| failure_window_mask[row.run_index] == in_failure_window)
1569 .filter(|row| preferred_labels.contains(&row.heuristic_name.as_str()))
1570 .count()
1571}
1572
1573fn preferred_grammar_hit_count(
1574 grammar_trace: &FeatureGrammarTrace,
1575 failure_window_mask: &[bool],
1576 in_failure_window: bool,
1577 preferred_states: &[&str],
1578) -> usize {
1579 if preferred_states.is_empty() {
1580 return 0;
1581 }
1582 grammar_trace
1583 .raw_states
1584 .iter()
1585 .enumerate()
1586 .filter(|(run_index, _)| failure_window_mask[*run_index] == in_failure_window)
1587 .filter(|(run_index, _)| {
1588 preferred_states.contains(&failure_grammar_state_label(grammar_trace, *run_index))
1589 })
1590 .count()
1591}
1592
1593fn linked_window_grounding_counts(
1594 bundle: &FeatureBundle<'_>,
1595 linked_failure_ids: &[usize],
1596 pre_failure_lookback_runs: usize,
1597 preferred_grounded_motif_types: &[&str],
1598) -> (BTreeMap<String, usize>, usize) {
1599 let mut counts = BTreeMap::<String, usize>::new();
1600 let mut preferred_window_count = 0usize;
1601 for &failure_index in linked_failure_ids {
1602 let start = failure_index.saturating_sub(pre_failure_lookback_runs);
1603 let grounded = grounded_motif_type_for_window(bundle, start, failure_index).to_string();
1604 if preferred_grounded_motif_types.contains(&grounded.as_str()) {
1605 preferred_window_count += 1;
1606 }
1607 *counts.entry(grounded).or_default() += 1;
1608 }
1609 (counts, preferred_window_count)
1610}
1611
1612fn linked_window_grammar_counts(
1613 grammar_trace: &FeatureGrammarTrace,
1614 linked_failure_ids: &[usize],
1615 pre_failure_lookback_runs: usize,
1616 preferred_grammar_states: &[&str],
1617) -> (BTreeMap<String, usize>, usize) {
1618 let mut counts = BTreeMap::<String, usize>::new();
1619 let mut preferred_window_count = 0usize;
1620 for &failure_index in linked_failure_ids {
1621 let start = failure_index.saturating_sub(pre_failure_lookback_runs);
1622 let dominant = dominant_grammar_state_in_window(grammar_trace, start, failure_index);
1623 if preferred_grammar_states.contains(&dominant.as_str()) {
1624 preferred_window_count += 1;
1625 }
1626 *counts.entry(dominant).or_default() += 1;
1627 }
1628 (counts, preferred_window_count)
1629}
1630
1631fn summarize_count_map(counts: &BTreeMap<String, usize>) -> String {
1632 if counts.is_empty() {
1633 "none".into()
1634 } else {
1635 counts
1636 .iter()
1637 .map(|(label, count)| format!("{label}:{count}"))
1638 .collect::<Vec<_>>()
1639 .join(";")
1640 }
1641}
1642
1643fn bracketed_csv_list(values: &[String]) -> String {
1644 if values.is_empty() {
1645 "[]".into()
1646 } else {
1647 format!("[{}]", values.join(","))
1648 }
1649}
1650
1651fn bracketed_usize_list(values: &[usize]) -> String {
1652 let items = values
1653 .iter()
1654 .map(|value| value.to_string())
1655 .collect::<Vec<_>>();
1656 bracketed_csv_list(&items)
1657}
1658
1659fn group_display_name(group_name: &str) -> &'static str {
1660 match group_name {
1661 "group_a" => "Group A",
1662 "group_b" => "Group B",
1663 "group_c" => "Group C",
1664 _ => "Unknown Group",
1665 }
1666}
1667
1668fn classify_feature_behavior(drift: &[f64], slew: &[f64]) -> &'static str {
1669 let mean_abs_drift = mean_abs(drift);
1670 let mean_abs_slew = mean_abs(slew);
1671 if mean_abs_drift < 0.02 && mean_abs_slew < 0.02 {
1672 "noise-like"
1673 } else if mean_abs_drift < 0.05 && mean_abs_slew < 0.05 {
1674 "stable"
1675 } else if mean_abs_slew > mean_abs_drift * 1.5 {
1676 "bursty"
1677 } else if drift.iter().any(|value| *value > 0.0) && drift.iter().any(|value| *value < 0.0) {
1678 "oscillatory"
1679 } else {
1680 "drifting"
1681 }
1682}
1683
1684fn build_minimal_heuristics_bank(
1685 missed_failure_diagnostics: &[MissedFailureDiagnosticRow],
1686 policy_operator_burden_contributions: &[OperatorBurdenContributionRow],
1687 feature_motif_grounding: &[FeatureMotifGroundingRecord],
1688) -> Vec<MinimalHeuristicEntry> {
1689 let grounding_by_name = feature_motif_grounding
1690 .iter()
1691 .map(|row| (row.feature_name.as_str(), row))
1692 .collect::<BTreeMap<_, _>>();
1693 let mut entries = Vec::new();
1694
1695 for diagnostic in missed_failure_diagnostics {
1696 let feature_name = match diagnostic.nearest_feature_name.as_deref() {
1697 Some("S134" | "S275") => diagnostic.nearest_feature_name.clone(),
1698 _ => diagnostic
1699 .optimized_feature_name
1700 .clone()
1701 .or_else(|| diagnostic.nearest_feature_name.clone()),
1702 };
1703 let motif_type = feature_name
1704 .as_deref()
1705 .and_then(|name| grounding_by_name.get(name).copied())
1706 .map(|row| row.motif_type.clone())
1707 .unwrap_or_else(|| "null".into());
1708 let (target_grammar_states, policy_action, status, burden_effect_class) =
1709 match diagnostic.exact_miss_rule.as_str() {
1710 "directional_consistency_gate" => (
1711 vec!["BoundaryGrazing".into(), "SustainedDrift".into()],
1712 "bounded Watch->Review promotion with persistence retained and corroboration relaxed only for this feature".into(),
1713 if diagnostic.recovered_after_optimization {
1714 "active_recovery".into()
1715 } else {
1716 "candidate_only".into()
1717 },
1718 "recall_recovery".into(),
1719 ),
1720 "watch_class_near_miss_below_numeric_gate" => (
1721 vec!["BoundaryGrazing".into()],
1722 if diagnostic.recovered_after_optimization {
1723 "priority rescue from Watch->Review under high EWMA occupancy and bounded fragmentation".into()
1724 } else {
1725 "reject rescue because the feature never develops sufficient grammar-qualified motif support".into()
1726 },
1727 if diagnostic.recovered_after_optimization {
1728 "active_recovery".into()
1729 } else {
1730 "rejected_for_insufficient_structure".into()
1731 },
1732 if diagnostic.recovered_after_optimization {
1733 "recall_recovery".into()
1734 } else {
1735 "bounded_rejection".into()
1736 },
1737 ),
1738 _ => (
1739 vec!["Admissible".into()],
1740 "no policy action accepted because the missed case lacks a recoverable DSFB structure".into(),
1741 "rejected_for_insufficient_structure".into(),
1742 "bounded_rejection".into(),
1743 ),
1744 };
1745
1746 entries.push(MinimalHeuristicEntry {
1747 heuristic_id: format!("failure_{}_{}", diagnostic.failure_run_index, diagnostic.exact_miss_rule),
1748 target_problem_type: "missed_failure".into(),
1749 target_identifier: diagnostic.failure_run_index.to_string(),
1750 target_feature_name: feature_name.clone(),
1751 target_motif_type: motif_type,
1752 target_grammar_states,
1753 semantic_requirement: "grammar-qualified semantic match required before any policy promotion".into(),
1754 policy_action,
1755 burden_effect_class,
1756 justification: format!(
1757 "Built from missed failure {} with nearest feature {:?}, operationalized feature {:?}, exact miss rule {}, bounded rescue recoverable={}.",
1758 diagnostic.failure_run_index,
1759 diagnostic.nearest_feature_name,
1760 diagnostic.optimized_feature_name,
1761 diagnostic.exact_miss_rule,
1762 diagnostic.bounded_rescue_would_recover,
1763 ),
1764 status,
1765 });
1766 }
1767
1768 let nuisance_entries = policy_operator_burden_contributions
1769 .iter()
1770 .filter(|row| {
1771 row.configuration_role == "baseline"
1772 && row.contribution_scope == "motif"
1773 && row.contribution_type == "review_escalate_burden"
1774 })
1775 .collect::<Vec<_>>();
1776 let mut nuisance_entries = nuisance_entries;
1777 nuisance_entries.sort_by(|left, right| {
1778 right
1779 .value
1780 .partial_cmp(&left.value)
1781 .unwrap_or(std::cmp::Ordering::Equal)
1782 });
1783 for nuisance in nuisance_entries.into_iter().take(4) {
1784 entries.push(MinimalHeuristicEntry {
1785 heuristic_id: format!("nuisance_{}", nuisance.name),
1786 target_problem_type: "nuisance_class".into(),
1787 target_identifier: nuisance.name.clone(),
1788 target_feature_name: None,
1789 target_motif_type: nuisance.name.clone(),
1790 target_grammar_states: match nuisance.name.as_str() {
1791 "recurrent_boundary_approach" => {
1792 vec!["BoundaryGrazing".into(), "SustainedDrift".into()]
1793 }
1794 "watch_only_boundary_grazing" => vec!["BoundaryGrazing".into()],
1795 _ => vec!["grammar_filtered".into()],
1796 },
1797 semantic_requirement: "apply only after grammar filtering and semantic retrieval".into(),
1798 policy_action: match nuisance.name.as_str() {
1799 "recurrent_boundary_approach" => {
1800 "keep default Watch ceiling unless persistence and bounded corroboration justify Review".into()
1801 }
1802 "watch_only_boundary_grazing" => {
1803 "retain Watch-only handling to suppress isolated burden".into()
1804 }
1805 _ => "suppress isolated nuisance contribution before Review/Escalate promotion".into(),
1806 },
1807 burden_effect_class: "nuisance_suppression".into(),
1808 justification: format!(
1809 "Derived from baseline nuisance pattern {} with {:.0} pass-run Review/Escalate points.",
1810 nuisance.name, nuisance.value
1811 ),
1812 status: "active_suppression".into(),
1813 });
1814 }
1815
1816 entries.sort_by(|left, right| left.heuristic_id.cmp(&right.heuristic_id));
1817 entries.truncate(MAX_MINIMAL_HEURISTICS);
1818 entries
1819}
1820
1821fn build_policy_burden_summary(
1822 dataset: &PreparedDataset,
1823 optimized_dsa: &DsaEvaluation,
1824) -> Vec<PolicyBurdenSummaryRow> {
1825 let mut rows = optimized_dsa
1826 .motif_policy_contributions
1827 .iter()
1828 .map(|row| PolicyBurdenSummaryRow {
1829 scope: "motif".into(),
1830 name: row.motif_name.clone(),
1831 watch_points: row.watch_points,
1832 review_points: row.review_points,
1833 escalate_points: row.escalate_points,
1834 pass_review_escalate_points: row.pass_review_or_escalate_points,
1835 pre_failure_review_escalate_points: row.pre_failure_review_or_escalate_points,
1836 silent_suppression_points: row.silent_suppression_points,
1837 justification: "optimized selected-row motif burden summary".into(),
1838 })
1839 .collect::<Vec<_>>();
1840
1841 let mut feature_rows = optimized_dsa
1842 .traces
1843 .iter()
1844 .filter_map(|trace| {
1845 let mut watch_points = 0usize;
1846 let mut review_points = 0usize;
1847 let mut escalate_points = 0usize;
1848 let mut pass_review_escalate_points = 0usize;
1849 let mut pre_failure_review_escalate_points = 0usize;
1850 let mut silent_suppression_points = 0usize;
1851 for run_index in 0..trace.policy_state.len() {
1852 match trace.policy_state[run_index] {
1853 DsaPolicyState::Silent => {
1854 if trace.policy_suppressed_to_silent[run_index] {
1855 silent_suppression_points += 1;
1856 }
1857 }
1858 DsaPolicyState::Watch => watch_points += 1,
1859 DsaPolicyState::Review => {
1860 review_points += 1;
1861 if dataset.labels[run_index] == -1 {
1862 pass_review_escalate_points += 1;
1863 }
1864 }
1865 DsaPolicyState::Escalate => {
1866 escalate_points += 1;
1867 if dataset.labels[run_index] == -1 {
1868 pass_review_escalate_points += 1;
1869 }
1870 }
1871 }
1872 if dataset.labels[run_index] == 1 && trace.dsa_alert[run_index] {
1873 pre_failure_review_escalate_points += 1;
1874 }
1875 }
1876 let investigation_points = review_points + escalate_points;
1877 (investigation_points > 0).then(|| PolicyBurdenSummaryRow {
1878 scope: "feature".into(),
1879 name: trace.feature_name.clone(),
1880 watch_points,
1881 review_points,
1882 escalate_points,
1883 pass_review_escalate_points,
1884 pre_failure_review_escalate_points,
1885 silent_suppression_points,
1886 justification: "optimized selected-row feature burden summary".into(),
1887 })
1888 })
1889 .collect::<Vec<_>>();
1890
1891 feature_rows.sort_by(|left, right| {
1892 (right.review_points + right.escalate_points)
1893 .cmp(&(left.review_points + left.escalate_points))
1894 .then_with(|| left.name.cmp(&right.name))
1895 });
1896 feature_rows.truncate(12);
1897 rows.extend(feature_rows);
1898 rows
1899}
1900
1901fn build_dsfb_vs_ewma_cases(
1902 dataset: &PreparedDataset,
1903 feature_bundles: &BTreeMap<usize, FeatureBundle<'_>>,
1904 optimized_by_failure: &BTreeMap<usize, &PerFailureRunDsaSignal>,
1905 metrics_by_failure: &BTreeMap<usize, &PerFailureRunSignal>,
1906 diagnostics_by_failure: &BTreeMap<usize, &MissedFailureDiagnosticRow>,
1907 missed_failure_diagnostics: &[MissedFailureDiagnosticRow],
1908 pre_failure_lookback_runs: usize,
1909) -> Vec<DsfbVsEwmaCase> {
1910 missed_failure_diagnostics
1911 .iter()
1912 .filter(|row| row.recovered_after_optimization)
1913 .filter_map(|diagnostic| {
1914 let feature_name = diagnostic
1915 .optimized_feature_name
1916 .clone()
1917 .or_else(|| diagnostic.nearest_feature_name.clone())?;
1918 let bundle = feature_bundles
1919 .values()
1920 .find(|row| row.residual.feature_name == feature_name)?;
1921 let optimized_row = optimized_by_failure.get(&diagnostic.failure_run_index).copied()?;
1922 let metrics_row = metrics_by_failure.get(&diagnostic.failure_run_index).copied()?;
1923 let start = diagnostic
1924 .failure_run_index
1925 .saturating_sub(pre_failure_lookback_runs);
1926 let window = (start..diagnostic.failure_run_index)
1927 .map(|run_index| DsfbVsEwmaWindowPoint {
1928 run_index,
1929 timestamp: dataset.timestamps[run_index]
1930 .format("%Y-%m-%d %H:%M:%S")
1931 .to_string(),
1932 label: dataset.labels[run_index],
1933 ewma_value: bundle.ewma.ewma[run_index],
1934 ewma_threshold: bundle.ewma.threshold,
1935 ewma_alarm: bundle.ewma.alarm[run_index],
1936 residual: bundle.residual.residuals[run_index],
1937 drift: bundle.sign.drift[run_index],
1938 slew: bundle.sign.slew[run_index],
1939 motif_label: bundle.motif.labels[run_index].as_lowercase().into(),
1940 grammar_state: failure_grammar_state_label(bundle.grammar, run_index).into(),
1941 policy_state: bundle.optimized_dsa.policy_state[run_index]
1942 .as_lowercase()
1943 .into(),
1944 })
1945 .collect::<Vec<_>>();
1946 let explanation = if metrics_row.earliest_ewma_run.is_some() {
1947 format!(
1948 "EWMA also detected failure {} with lead {:?}, so DSFB does not add scalar coverage here. The DSFB recovery adds structure instead: feature {} shows motif {} under grammar {} and is promoted by a bounded rescue tied to {}.",
1949 diagnostic.failure_run_index,
1950 metrics_row.ewma_lead_runs,
1951 feature_name,
1952 dominant_motif_in_window(bundle.motif, start, diagnostic.failure_run_index),
1953 dominant_grammar_state_in_window(bundle.grammar, start, diagnostic.failure_run_index),
1954 diagnostic.exact_miss_rule,
1955 )
1956 } else {
1957 format!(
1958 "EWMA missed failure {}, while DSFB recovered it through feature {} with motif {} under grammar {}.",
1959 diagnostic.failure_run_index,
1960 feature_name,
1961 dominant_motif_in_window(bundle.motif, start, diagnostic.failure_run_index),
1962 dominant_grammar_state_in_window(bundle.grammar, start, diagnostic.failure_run_index),
1963 )
1964 };
1965 let _ = diagnostics_by_failure;
1966
1967 Some(DsfbVsEwmaCase {
1968 failure_id: diagnostic.failure_run_index,
1969 failure_timestamp: optimized_row.failure_timestamp.clone(),
1970 recovered: true,
1971 recovered_feature_name: feature_name,
1972 baseline_miss_rule: diagnostic.exact_miss_rule.clone(),
1973 ewma_detected: metrics_row.earliest_ewma_run.is_some(),
1974 ewma_lead_runs: metrics_row.ewma_lead_runs,
1975 optimized_dsa_lead_runs: optimized_row.dsa_lead_runs,
1976 explanation,
1977 window,
1978 })
1979 })
1980 .collect()
1981}
1982
1983fn top_feature_activity_for_failure(
1984 feature_bundles: &BTreeMap<usize, FeatureBundle<'_>>,
1985 failure_index: usize,
1986 pre_failure_lookback_runs: usize,
1987 limit: usize,
1988 require_nonzero_score: bool,
1989) -> Vec<FeatureActivityCandidate> {
1990 let start = failure_index.saturating_sub(pre_failure_lookback_runs);
1991 let mut candidates = feature_bundles
1992 .iter()
1993 .map(|(&feature_index, bundle)| {
1994 let max_dsa_score = bundle.baseline_dsa.dsa_score[start..failure_index]
1995 .iter()
1996 .copied()
1997 .fold(0.0, f64::max);
1998 let motif_hits = bundle.baseline_dsa.motif_hit[start..failure_index]
1999 .iter()
2000 .filter(|flag| **flag)
2001 .count();
2002 let pressure_hits = (start..failure_index)
2003 .filter(|&run_index| {
2004 matches!(
2005 failure_grammar_state_label(bundle.grammar, run_index),
2006 "BoundaryGrazing"
2007 | "SustainedDrift"
2008 | "TransientViolation"
2009 | "PersistentViolation"
2010 )
2011 })
2012 .count();
2013 let max_policy_state = dominant_policy_state_in_window(
2014 &bundle.baseline_dsa.policy_state[start..failure_index],
2015 )
2016 .into();
2017 FeatureActivityCandidate {
2018 feature_index,
2019 feature_name: bundle.residual.feature_name.clone(),
2020 ranking_score_proxy: max_dsa_score
2021 + 0.25 * motif_hits as f64
2022 + 0.25 * pressure_hits as f64,
2023 max_dsa_score,
2024 max_policy_state,
2025 motif_hits,
2026 pressure_hits,
2027 }
2028 })
2029 .filter(|candidate| !require_nonzero_score || candidate.ranking_score_proxy > 0.0)
2030 .collect::<Vec<_>>();
2031 candidates.sort_by(|left, right| {
2032 right
2033 .ranking_score_proxy
2034 .partial_cmp(&left.ranking_score_proxy)
2035 .unwrap_or(std::cmp::Ordering::Equal)
2036 .then_with(|| left.feature_name.cmp(&right.feature_name))
2037 });
2038 candidates.truncate(limit);
2039 candidates
2040}
2041
2042fn semantic_labels_in_window(
2043 semantic_layer: &SemanticLayer,
2044 feature_name: &str,
2045 start: usize,
2046 failure_index: usize,
2047) -> Vec<String> {
2048 let mut labels = semantic_layer
2049 .semantic_matches
2050 .iter()
2051 .filter(|row| {
2052 row.feature_name == feature_name
2053 && row.run_index >= start
2054 && row.run_index < failure_index
2055 })
2056 .map(|row| row.heuristic_name.clone())
2057 .collect::<BTreeSet<_>>()
2058 .into_iter()
2059 .collect::<Vec<_>>();
2060 if labels.is_empty() {
2061 labels.push("none".into());
2062 }
2063 labels
2064}
2065
2066fn feature_failure_explanation(
2067 bundle: &FeatureBundle<'_>,
2068 start: usize,
2069 failure_index: usize,
2070 diagnostic: Option<&MissedFailureDiagnosticRow>,
2071 semantic_labels: &[String],
2072) -> (String, String) {
2073 let has_non_stable_motif = bundle.motif.labels[start..failure_index]
2074 .iter()
2075 .any(|label| *label != DsfbMotifClass::StableAdmissible);
2076 let has_pressure = (start..failure_index).any(|run_index| {
2077 matches!(
2078 failure_grammar_state_label(bundle.grammar, run_index),
2079 "BoundaryGrazing" | "SustainedDrift" | "TransientViolation" | "PersistentViolation"
2080 )
2081 });
2082 let has_semantic = semantic_labels.iter().any(|label| label != "none");
2083 let has_review = bundle.baseline_dsa.policy_state[start..failure_index]
2084 .iter()
2085 .any(|state| matches!(state, DsaPolicyState::Review | DsaPolicyState::Escalate));
2086
2087 if !has_non_stable_motif {
2088 (
2089 "syntax".into(),
2090 "No non-trivial DSFB motif was detected in the failure lookback window.".into(),
2091 )
2092 } else if !has_pressure {
2093 (
2094 "grammar".into(),
2095 "A motif candidate was present but never qualified by a non-admissible envelope interaction.".into(),
2096 )
2097 } else if !has_semantic {
2098 (
2099 "semantics".into(),
2100 "Syntax and grammar were present, but no grammar-qualified semantic retrieval survived the heuristics bank.".into(),
2101 )
2102 } else if !has_review {
2103 (
2104 "policy".into(),
2105 diagnostic
2106 .map(|row| {
2107 format!(
2108 "Semantic evidence existed but policy never promoted beyond {} because of {}.",
2109 row.nearest_feature_policy_state
2110 .clone()
2111 .unwrap_or_else(|| "silent".into()),
2112 row.exact_miss_rule
2113 )
2114 })
2115 .unwrap_or_else(|| {
2116 "Semantic evidence existed but policy never promoted to Review/Escalate.".into()
2117 }),
2118 )
2119 } else {
2120 (
2121 "support".into(),
2122 "The feature carried supportive structure but was not the decisive failure-localized signal.".into(),
2123 )
2124 }
2125}
2126
2127fn failure_stage_label(diagnostic: Option<&MissedFailureDiagnosticRow>) -> String {
2128 match diagnostic {
2129 Some(row) if row.ranking_exclusion => "ranking".into(),
2130 Some(row) if row.cohort_selection => "cohort_selection".into(),
2131 Some(row) if row.policy_suppression => "policy".into(),
2132 Some(row) if row.fragmentation_ceiling => "policy_fragmentation".into(),
2133 Some(row) if row.directional_consistency_gate => "policy_directionality".into(),
2134 Some(row) if row.persistence_gate => "policy_persistence".into(),
2135 Some(row) if row.corroboration_threshold => "policy_corroboration".into(),
2136 Some(row) if row.rescue_gate_not_activating => "rescue".into(),
2137 Some(_) => "policy".into(),
2138 None => "unclassified".into(),
2139 }
2140}
2141
2142fn failure_explanation_text(
2143 diagnostic: Option<&MissedFailureDiagnosticRow>,
2144 optimized_row: &PerFailureRunDsaSignal,
2145) -> String {
2146 match diagnostic {
2147 Some(row) if row.recovered_after_optimization => format!(
2148 "Baseline DSA missed the failure because of {} on feature {:?}; the bounded recovery layer later recovered it via {:?}.",
2149 row.exact_miss_rule, row.nearest_feature_name, row.optimized_feature_name
2150 ),
2151 Some(row) => format!(
2152 "Baseline DSA missed the failure because of {} on feature {:?}; no bounded recovery rule recovered it.",
2153 row.exact_miss_rule, row.nearest_feature_name
2154 ),
2155 None if optimized_row.earliest_dsa_run.is_some() => {
2156 "The failure was recovered during optimization, but no baseline miss diagnostic row was available.".into()
2157 }
2158 None => "The failure remains missed and no detailed diagnostic row was available.".into(),
2159 }
2160}
2161
2162fn miss_reason_class(
2163 diagnostic: Option<&MissedFailureDiagnosticRow>,
2164 optimized_row: &PerFailureRunDsaSignal,
2165) -> &'static str {
2166 match diagnostic {
2167 Some(row) if row.ranking_exclusion || row.cohort_selection => "feature_exclusion",
2168 Some(row) if row.fragmentation_ceiling => "fragmentation_ceiling",
2169 Some(row)
2170 if row.policy_suppression
2171 || row.directional_consistency_gate
2172 || row.persistence_gate
2173 || row.corroboration_threshold
2174 || row.rescue_gate_not_activating =>
2175 {
2176 "policy_suppression"
2177 }
2178 Some(_) if optimized_row.earliest_dsa_run.is_none() => "semantic_unresolved",
2179 Some(_) => "grammar_rejection",
2180 None if optimized_row.earliest_dsa_run.is_some() => "recoverable",
2181 None => "feature_absence",
2182 }
2183}
2184
2185fn scalar_detection_explanation(
2186 layer_name: &str,
2187 detected: bool,
2188 lead_runs: Option<usize>,
2189 supporting_features: &[String],
2190) -> String {
2191 if detected {
2192 format!(
2193 "{} detected the failure with lead {:?}; supporting features in the lookback were {}.",
2194 layer_name,
2195 lead_runs,
2196 if supporting_features.is_empty() {
2197 "none".into()
2198 } else {
2199 supporting_features.join(",")
2200 }
2201 )
2202 } else {
2203 format!("{layer_name} did not detect the failure in the configured lookback window.")
2204 }
2205}
2206
2207fn scalar_supporting_features(
2208 feature_bundles: &BTreeMap<usize, FeatureBundle<'_>>,
2209 start: usize,
2210 failure_index: usize,
2211 mode: ScalarSupportMode,
2212) -> Vec<String> {
2213 let mut scored = feature_bundles
2214 .values()
2215 .map(|bundle| {
2216 let score = match mode {
2217 ScalarSupportMode::Threshold => bundle.residual.threshold_alarm
2218 [start..failure_index]
2219 .iter()
2220 .filter(|flag| **flag)
2221 .count(),
2222 ScalarSupportMode::Ewma => bundle.ewma.alarm[start..failure_index]
2223 .iter()
2224 .filter(|flag| **flag)
2225 .count(),
2226 };
2227 (bundle.residual.feature_name.clone(), score)
2228 })
2229 .filter(|(_, score)| *score > 0)
2230 .collect::<Vec<_>>();
2231 scored.sort_by(|left, right| right.1.cmp(&left.1).then_with(|| left.0.cmp(&right.0)));
2232 scored.into_iter().take(3).map(|(name, _)| name).collect()
2233}
2234
2235fn grounding_candidate_features(
2236 baseline_dsa: &DsaEvaluation,
2237 optimized_dsa: &DsaEvaluation,
2238 missed_failure_diagnostics: &[MissedFailureDiagnosticRow],
2239 policy_operator_burden_contributions: &[OperatorBurdenContributionRow],
2240 failure_cases: &[FailureCaseReport],
2241) -> Vec<usize> {
2242 let mut features = BTreeSet::new();
2243 for spec in FEATURE_ROLE_LOCK {
2244 if let Some(feature_index) = feature_index_from_name(spec.feature_name) {
2245 features.insert(feature_index);
2246 }
2247 }
2248 for row in &baseline_dsa.per_failure_run_signals {
2249 if let Some(feature_index) = row.max_dsa_score_feature_index {
2250 features.insert(feature_index);
2251 }
2252 if let Some(feature_index) = row.earliest_dsa_feature_index {
2253 features.insert(feature_index);
2254 }
2255 }
2256 for row in &optimized_dsa.per_failure_run_signals {
2257 if let Some(feature_index) = row.earliest_dsa_feature_index {
2258 features.insert(feature_index);
2259 }
2260 }
2261 for row in missed_failure_diagnostics {
2262 if let Some(feature_name) = row.nearest_feature_name.as_deref() {
2263 if let Some(index) = feature_index_from_name(feature_name) {
2264 features.insert(index);
2265 }
2266 }
2267 if let Some(feature_name) = row.optimized_feature_name.as_deref() {
2268 if let Some(index) = feature_index_from_name(feature_name) {
2269 features.insert(index);
2270 }
2271 }
2272 }
2273 for case in failure_cases {
2274 for feature in &case.top_contributing_features {
2275 features.insert(feature.feature_index);
2276 }
2277 }
2278 let mut burden_features = policy_operator_burden_contributions
2279 .iter()
2280 .filter(|row| row.configuration_role == "optimized" && row.contribution_scope == "feature")
2281 .filter_map(|row| feature_index_from_name(&row.name).map(|index| (index, row.value)))
2282 .collect::<Vec<_>>();
2283 burden_features.sort_by(|left, right| {
2284 right
2285 .1
2286 .partial_cmp(&left.1)
2287 .unwrap_or(std::cmp::Ordering::Equal)
2288 });
2289 for (feature_index, _) in burden_features.into_iter().take(12) {
2290 features.insert(feature_index);
2291 }
2292 features.into_iter().collect()
2293}
2294
2295fn semantic_hit_count(
2296 semantic_layer: &SemanticLayer,
2297 feature_name: &str,
2298 failure_window_mask: &[bool],
2299 in_failure_window: bool,
2300) -> usize {
2301 semantic_layer
2302 .semantic_matches
2303 .iter()
2304 .filter(|row| {
2305 row.feature_name == feature_name
2306 && failure_window_mask[row.run_index] == in_failure_window
2307 })
2308 .count()
2309}
2310
2311fn pressure_hit_count(
2312 grammar_trace: &FeatureGrammarTrace,
2313 failure_window_mask: &[bool],
2314 in_failure_window: bool,
2315) -> usize {
2316 grammar_trace
2317 .raw_states
2318 .iter()
2319 .enumerate()
2320 .filter(|(run_index, _)| failure_window_mask[*run_index] == in_failure_window)
2321 .filter(|(run_index, _)| {
2322 matches!(
2323 failure_grammar_state_label(grammar_trace, *run_index),
2324 "BoundaryGrazing" | "SustainedDrift" | "TransientViolation" | "PersistentViolation"
2325 )
2326 })
2327 .count()
2328}
2329
2330fn masked_mean_abs(values: &[f64], mask: &[bool], select_value: bool) -> f64 {
2331 let selected = values
2332 .iter()
2333 .zip(mask)
2334 .filter(|(_, flag)| **flag == select_value)
2335 .map(|(value, _)| value.abs())
2336 .collect::<Vec<_>>();
2337 mean(&selected).unwrap_or(0.0)
2338}
2339
2340fn dominant_motif_for_mask(
2341 motif_trace: &FeatureMotifTrace,
2342 mask: &[bool],
2343 select_value: bool,
2344) -> String {
2345 let mut counts = BTreeMap::<&'static str, usize>::new();
2346 for (run_index, label) in motif_trace.labels.iter().enumerate() {
2347 if mask[run_index] != select_value {
2348 continue;
2349 }
2350 *counts.entry(label.as_lowercase()).or_default() += 1;
2351 }
2352 counts
2353 .into_iter()
2354 .max_by(|left, right| left.1.cmp(&right.1).then_with(|| right.0.cmp(left.0)))
2355 .map(|(label, _)| label.to_string())
2356 .unwrap_or_else(|| "stable_admissible".into())
2357}
2358
2359fn dominant_grammar_state_for_mask(
2360 grammar_trace: &FeatureGrammarTrace,
2361 mask: &[bool],
2362 select_value: bool,
2363) -> String {
2364 let mut counts = BTreeMap::<&'static str, usize>::new();
2365 for run_index in 0..grammar_trace.raw_states.len() {
2366 if mask[run_index] != select_value {
2367 continue;
2368 }
2369 *counts
2370 .entry(failure_grammar_state_label(grammar_trace, run_index))
2371 .or_default() += 1;
2372 }
2373 counts
2374 .into_iter()
2375 .max_by(|left, right| left.1.cmp(&right.1).then_with(|| right.0.cmp(left.0)))
2376 .map(|(label, _)| label.to_string())
2377 .unwrap_or_else(|| "Admissible".into())
2378}
2379
2380fn grounded_motif_type(
2381 dominant_dsfb_motif: &str,
2382 dominant_grammar_state: &str,
2383 failure_local_recovery_case_count: usize,
2384 failure_semantic_hits: usize,
2385 pass_semantic_hits: usize,
2386 failure_pressure_hits: usize,
2387 pass_pressure_hits: usize,
2388 mean_abs_drift_failure: f64,
2389 mean_abs_slew_failure: f64,
2390) -> &'static str {
2391 if failure_semantic_hits == 0 && failure_pressure_hits == 0 {
2392 return "null";
2393 }
2394 if (dominant_grammar_state == "Recovery" && failure_semantic_hits > 0)
2395 || (failure_local_recovery_case_count > 0 && failure_semantic_hits >= pass_semantic_hits)
2396 {
2397 return "recovery_pattern";
2398 }
2399 if pass_semantic_hits > failure_semantic_hits.saturating_mul(3) && failure_pressure_hits <= 1 {
2400 return "noise_like";
2401 }
2402 match dominant_dsfb_motif {
2403 "pre_failure_slow_drift" => "slow_drift_precursor",
2404 "recurrent_boundary_approach" | "watch_only_boundary_grazing" => "boundary_grazing",
2405 "transition_excursion" => {
2406 if mean_abs_slew_failure > mean_abs_drift_failure {
2407 "transient_excursion"
2408 } else {
2409 "burst_instability"
2410 }
2411 }
2412 "persistent_instability_cluster" => "persistent_instability",
2413 "transition_cluster_support" => "burst_instability",
2414 _ if failure_pressure_hits > pass_pressure_hits
2415 && mean_abs_drift_failure > mean_abs_slew_failure =>
2416 {
2417 "slow_drift_precursor"
2418 }
2419 _ if dominant_grammar_state == "BoundaryGrazing" => "boundary_grazing",
2420 _ if failure_pressure_hits > pass_pressure_hits => "persistent_instability",
2421 _ => "noise_like",
2422 }
2423}
2424
2425fn grounded_motif_type_for_window(
2426 bundle: &FeatureBundle<'_>,
2427 start: usize,
2428 failure_index: usize,
2429) -> &'static str {
2430 let window_mask = (0..bundle.motif.labels.len())
2431 .map(|run_index| run_index >= start && run_index < failure_index)
2432 .collect::<Vec<_>>();
2433 grounded_motif_type(
2434 dominant_motif_for_mask(bundle.motif, &window_mask, true).as_str(),
2435 dominant_grammar_state_in_window(bundle.grammar, start, failure_index).as_str(),
2436 0,
2437 bundle.motif.labels[start..failure_index]
2438 .iter()
2439 .filter(|label| **label != DsfbMotifClass::StableAdmissible)
2440 .count(),
2441 0,
2442 pressure_hit_count(bundle.grammar, &window_mask, true),
2443 0,
2444 mean_abs(&bundle.sign.drift[start..failure_index]),
2445 mean_abs(&bundle.sign.slew[start..failure_index]),
2446 )
2447}
2448
2449fn build_failure_window_mask(
2450 len: usize,
2451 failure_indices: &[usize],
2452 pre_failure_lookback_runs: usize,
2453) -> Vec<bool> {
2454 let mut mask = vec![false; len];
2455 for &failure_index in failure_indices {
2456 let start = failure_index.saturating_sub(pre_failure_lookback_runs);
2457 for flag in &mut mask[start..failure_index] {
2458 *flag = true;
2459 }
2460 }
2461 mask
2462}
2463
2464fn dominant_motif_in_window(
2465 motif_trace: &FeatureMotifTrace,
2466 start: usize,
2467 failure_index: usize,
2468) -> String {
2469 let mask = (0..motif_trace.labels.len())
2470 .map(|run_index| run_index >= start && run_index < failure_index)
2471 .collect::<Vec<_>>();
2472 dominant_motif_for_mask(motif_trace, &mask, true)
2473}
2474
2475fn dominant_grammar_state_in_window(
2476 grammar_trace: &FeatureGrammarTrace,
2477 start: usize,
2478 failure_index: usize,
2479) -> String {
2480 let mut counts = BTreeMap::<&'static str, usize>::new();
2481 for run_index in start..failure_index {
2482 *counts
2483 .entry(failure_grammar_state_label(grammar_trace, run_index))
2484 .or_default() += 1;
2485 }
2486 counts
2487 .into_iter()
2488 .max_by(|left, right| left.1.cmp(&right.1).then_with(|| right.0.cmp(left.0)))
2489 .map(|(label, _)| label.to_string())
2490 .unwrap_or_else(|| "Admissible".into())
2491}
2492
2493fn dominant_policy_state_in_window(states: &[DsaPolicyState]) -> &'static str {
2494 if states
2495 .iter()
2496 .any(|state| matches!(state, DsaPolicyState::Escalate))
2497 {
2498 "escalate"
2499 } else if states
2500 .iter()
2501 .any(|state| matches!(state, DsaPolicyState::Review))
2502 {
2503 "review"
2504 } else if states
2505 .iter()
2506 .any(|state| matches!(state, DsaPolicyState::Watch))
2507 {
2508 "watch"
2509 } else {
2510 "silent"
2511 }
2512}
2513
2514fn failure_grammar_state_label(
2515 grammar_trace: &FeatureGrammarTrace,
2516 run_index: usize,
2517) -> &'static str {
2518 if grammar_trace.raw_states[run_index] == GrammarState::Violation {
2519 if grammar_trace.persistent_violation[run_index] {
2520 "PersistentViolation"
2521 } else {
2522 "TransientViolation"
2523 }
2524 } else if grammar_trace.raw_states[run_index] == GrammarState::Boundary {
2525 if grammar_trace.raw_reasons[run_index] == GrammarReason::SustainedOutwardDrift {
2526 "SustainedDrift"
2527 } else {
2528 "BoundaryGrazing"
2529 }
2530 } else if run_index > 0
2531 && matches!(
2532 failure_grammar_state_label(grammar_trace, run_index - 1),
2533 "BoundaryGrazing" | "SustainedDrift" | "TransientViolation" | "PersistentViolation"
2534 )
2535 {
2536 "Recovery"
2537 } else {
2538 "Admissible"
2539 }
2540}
2541
2542fn feature_index_from_name(feature_name: &str) -> Option<usize> {
2543 feature_name
2544 .strip_prefix('S')
2545 .and_then(|value| value.parse::<usize>().ok())
2546 .and_then(|value| value.checked_sub(1))
2547}
2548
2549fn mean(values: &[f64]) -> Option<f64> {
2550 (!values.is_empty()).then_some(values.iter().sum::<f64>() / values.len() as f64)
2551}
2552
2553fn mean_abs(values: &[f64]) -> f64 {
2554 if values.is_empty() {
2555 0.0
2556 } else {
2557 values.iter().map(|value| value.abs()).sum::<f64>() / values.len() as f64
2558 }
2559}
2560
2561fn episode_ranges(signal: &[bool]) -> Vec<(usize, usize)> {
2562 let mut episodes = Vec::new();
2563 let mut start = None::<usize>;
2564 for (run_index, flag) in signal.iter().copied().enumerate() {
2565 match (start, flag) {
2566 (None, true) => start = Some(run_index),
2567 (Some(episode_start), false) => {
2568 episodes.push((episode_start, run_index - 1));
2569 start = None;
2570 }
2571 _ => {}
2572 }
2573 }
2574 if let Some(episode_start) = start {
2575 episodes.push((episode_start, signal.len().saturating_sub(1)));
2576 }
2577 episodes
2578}
2579
2580enum ScalarSupportMode {
2581 Threshold,
2582 Ewma,
2583}
2584
2585#[cfg(test)]
2586mod tests {
2587 use super::*;
2588
2589 #[test]
2590 fn grouped_semiotics_rejection_helper_is_deterministic() {
2591 let definitions = vec![
2592 GroupDefinitionRecord {
2593 group_name: "group_a".into(),
2594 member_features: "S001,S002".into(),
2595 member_roles: "a,b".into(),
2596 preferred_motifs: "slow_drift_precursor".into(),
2597 empirical_basis: "fixture".into(),
2598 group_size: 2,
2599 rescue_eligible_member_count: 1,
2600 highest_rescue_priority: "high".into(),
2601 semantic_match_count: 0,
2602 dominant_group_heuristic: None,
2603 pressure_run_count: 0,
2604 violation_run_count: 0,
2605 mean_active_feature_count: 0.0,
2606 mean_envelope_separation: 0.0,
2607 coactivation_member_threshold: 2,
2608 minimum_failure_coactivation_runs: 2,
2609 failure_coactivation_run_count: 1,
2610 pass_coactivation_run_count: 0,
2611 validated: false,
2612 rejection_reason: Some("failure co-activation below threshold".into()),
2613 },
2614 GroupDefinitionRecord {
2615 group_name: "group_b".into(),
2616 member_features: "S003,S004".into(),
2617 member_roles: "c,d".into(),
2618 preferred_motifs: "persistent_instability".into(),
2619 empirical_basis: "fixture".into(),
2620 group_size: 2,
2621 rescue_eligible_member_count: 1,
2622 highest_rescue_priority: "medium".into(),
2623 semantic_match_count: 3,
2624 dominant_group_heuristic: Some("persistent_instability_cluster".into()),
2625 pressure_run_count: 3,
2626 violation_run_count: 1,
2627 mean_active_feature_count: 2.0,
2628 mean_envelope_separation: 0.8,
2629 coactivation_member_threshold: 2,
2630 minimum_failure_coactivation_runs: 2,
2631 failure_coactivation_run_count: 2,
2632 pass_coactivation_run_count: 0,
2633 validated: true,
2634 rejection_reason: None,
2635 },
2636 ];
2637
2638 assert!(!grouped_semiotics_rejected(&definitions));
2639 let validated = validated_group_definitions(&definitions);
2640 assert_eq!(validated.len(), 1);
2641 assert_eq!(validated[0].group_name, "group_b");
2642 }
2643
2644 #[test]
2645 fn grounded_motif_type_prefers_null_without_failure_structure() {
2646 assert_eq!(
2647 grounded_motif_type("stable_admissible", "Admissible", 0, 0, 0, 0, 0, 0.0, 0.0),
2648 "null"
2649 );
2650 assert_eq!(
2651 grounded_motif_type(
2652 "recurrent_boundary_approach",
2653 "BoundaryGrazing",
2654 0,
2655 2,
2656 10,
2657 1,
2658 5,
2659 0.1,
2660 0.1
2661 ),
2662 "noise_like"
2663 );
2664 assert_eq!(
2665 grounded_motif_type(
2666 "persistent_instability_cluster",
2667 "Recovery",
2668 0,
2669 3,
2670 1,
2671 2,
2672 0,
2673 0.1,
2674 0.2
2675 ),
2676 "recovery_pattern"
2677 );
2678 assert_eq!(
2679 grounded_motif_type("stable_admissible", "Admissible", 1, 4, 1, 6, 0, 0.1, 0.2),
2680 "recovery_pattern"
2681 );
2682 }
2683}