use crate::grammar::{FeatureGrammarTrace, GrammarReason, GrammarSet, GrammarState};
use crate::heuristics::{
expanded_semantic_policy_definitions, heuristic_policy_definition, HeuristicAlertClass,
PERSISTENT_INSTABILITY_CLUSTER, PRE_FAILURE_SLOW_DRIFT, RECURRENT_BOUNDARY_APPROACH,
TRANSIENT_EXCURSION, TRANSITION_CLUSTER_SUPPORT, TRANSITION_EXCURSION,
WATCH_ONLY_BOUNDARY_GRAZING,
};
use crate::nominal::NominalModel;
use crate::preprocessing::PreparedDataset;
use crate::residual::{ResidualFeatureTrace, ResidualSet};
use crate::signs::{FeatureSigns, SignSet};
use serde::Serialize;
use std::collections::{BTreeMap, BTreeSet};
#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum DsfbMotifClass {
StableAdmissible,
RecurrentBoundaryApproach,
PreFailureSlowDrift,
TransitionExcursion,
PersistentInstabilityCluster,
TransitionClusterSupport,
WatchOnlyBoundaryGrazing,
}
impl DsfbMotifClass {
pub fn as_lowercase(self) -> &'static str {
match self {
Self::StableAdmissible => "stable_admissible",
Self::RecurrentBoundaryApproach => "recurrent_boundary_approach",
Self::PreFailureSlowDrift => "pre_failure_slow_drift",
Self::TransitionExcursion => "transition_excursion",
Self::PersistentInstabilityCluster => "persistent_instability_cluster",
Self::TransitionClusterSupport => "transition_cluster_support",
Self::WatchOnlyBoundaryGrazing => "watch_only_boundary_grazing",
}
}
pub fn definition(self) -> &'static str {
match self {
Self::StableAdmissible => "Low residual magnitude, low drift, and low slew inside the admissibility envelope.",
Self::RecurrentBoundaryApproach => "Repeated boundary proximity with outward structural tendency and bounded fragmentation.",
Self::PreFailureSlowDrift => "Persistent outward drift with moderate residual growth and limited abrupt slew.",
Self::TransitionExcursion => "Elevated slew burst aligned with a grammar transition or violation onset.",
Self::PersistentInstabilityCluster => "Repeated or sustained outward grammar pressure that is not reducible to isolated spikes.",
Self::TransitionClusterSupport => "Corroborating burst or pressure feature aligned with a primary structural transition.",
Self::WatchOnlyBoundaryGrazing => "Boundary proximity without sufficient persistence or corroboration for Review promotion.",
}
}
}
#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ScaffoldGrammarState {
Admissible,
BoundaryGrazing,
SustainedOutwardDrift,
TransientViolation,
PersistentViolation,
Recovery,
}
impl ScaffoldGrammarState {
pub fn as_lowercase(self) -> &'static str {
match self {
Self::Admissible => "admissible",
Self::BoundaryGrazing => "boundary_grazing",
Self::SustainedOutwardDrift => "sustained_outward_drift",
Self::TransientViolation => "transient_violation",
Self::PersistentViolation => "persistent_violation",
Self::Recovery => "recovery",
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct FeatureMotifTrace {
pub feature_index: usize,
pub feature_name: String,
pub labels: Vec<DsfbMotifClass>,
}
#[derive(Debug, Clone, Serialize)]
pub struct MotifSummaryRow {
pub motif_label: String,
pub definition: String,
pub point_hits: usize,
pub pre_failure_point_hits: usize,
}
#[derive(Debug, Clone, Serialize)]
pub struct SemanticMatchRecord {
pub feature_index: usize,
pub feature_name: String,
pub feature_role: String,
pub run_index: usize,
pub timestamp: String,
pub label: i8,
pub grammar_state: String,
pub grammar_reason: String,
pub motif_label: String,
pub heuristic_name: String,
pub alert_class_default: String,
pub grammar_constraints: String,
pub regime_conditions: String,
pub applicability_rules: String,
pub feature_scope: String,
pub ambiguity_note: String,
pub rescue_eligibility_guidance: String,
pub burden_contribution_class: String,
pub structural_score_proxy: f64,
pub rank: usize,
}
#[derive(Debug, Clone, Serialize)]
pub struct StructuralDeltaMetrics {
pub grammar_violation_precision: Option<f64>,
pub motif_precision_pre_failure: Option<f64>,
pub structural_separation: Option<f64>,
pub precursor_stability: Option<f64>,
pub episode_precision: Option<f64>,
pub compression_ratio: Option<f64>,
}
#[derive(Debug, Clone, Serialize)]
pub struct MotifSet {
pub traces: Vec<FeatureMotifTrace>,
pub summary_rows: Vec<MotifSummaryRow>,
}
#[derive(Debug, Clone, Serialize)]
pub struct SemanticLayer {
pub semantic_matches: Vec<SemanticMatchRecord>,
pub ranked_candidates: Vec<SemanticMatchRecord>,
pub structural_delta_metrics: StructuralDeltaMetrics,
}
#[derive(Debug, Clone)]
pub struct FeatureSemanticFlags {
pub semantic_flags: BTreeMap<&'static str, Vec<bool>>,
pub any_semantic_match: Vec<bool>,
}
#[derive(Debug, Clone, Serialize)]
pub struct FeatureSignRecord {
pub feature_index: usize,
pub feature_name: String,
pub feature_role: String,
pub group_name: String,
pub run_index: usize,
pub timestamp: String,
pub label: i8,
pub normalized_residual: f64,
pub drift: f64,
pub slew: f64,
pub normalized_residual_norm: f64,
pub sigma_norm: f64,
pub is_imputed: bool,
}
#[derive(Debug, Clone, Serialize)]
pub struct FeatureMotifTimelineRecord {
pub feature_index: usize,
pub feature_name: String,
pub feature_role: String,
pub group_name: String,
pub run_index: usize,
pub timestamp: String,
pub label: i8,
pub motif_label: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct FeatureGrammarStateRecord {
pub feature_index: usize,
pub feature_name: String,
pub feature_role: String,
pub group_name: String,
pub run_index: usize,
pub timestamp: String,
pub label: i8,
pub grammar_state: String,
pub raw_state: String,
pub confirmed_state: String,
pub raw_reason: String,
pub confirmed_reason: String,
pub normalized_envelope_ratio: f64,
pub persistent_boundary: bool,
pub persistent_violation: bool,
pub suppressed_by_imputation: bool,
}
#[derive(Debug, Clone, Serialize)]
pub struct EnvelopeInteractionSummaryRow {
pub feature_index: usize,
pub feature_name: String,
pub feature_role: String,
pub group_name: String,
pub boundary_grazing_points: usize,
pub sustained_outward_drift_points: usize,
pub transient_violation_points: usize,
pub persistent_violation_points: usize,
pub recovery_points: usize,
pub max_normalized_envelope_ratio: f64,
pub mean_normalized_envelope_ratio: f64,
}
#[derive(Debug, Clone, Serialize)]
pub struct ExpandedHeuristicEntry {
pub heuristic_name: String,
pub motif_signature: String,
pub allowed_grammar_states: String,
pub role_class: String,
pub feature_scope: String,
pub interpretation_text: String,
pub ambiguity_note: String,
pub rescue_eligibility_guidance: String,
pub burden_contribution_class: String,
pub alert_class_default: String,
pub requires_persistence: bool,
pub requires_corroboration: bool,
pub minimum_window: usize,
pub minimum_hits: usize,
pub maximum_allowed_fragmentation: f64,
}
#[derive(Debug, Clone, Serialize)]
pub struct FeaturePolicyDecisionRecord {
pub feature_index: usize,
pub feature_name: String,
pub feature_role: String,
pub group_name: String,
pub run_index: usize,
pub timestamp: String,
pub label: i8,
pub grammar_state: String,
pub motif_label: String,
pub semantic_label: Option<String>,
pub policy_ceiling: String,
pub policy_state: String,
pub investigation_worthy: bool,
pub corroborated: bool,
pub corroborated_by: String,
pub rationale: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct GroupSignRecord {
pub group_name: String,
pub run_index: usize,
pub timestamp: String,
pub label: i8,
pub active_feature_count: usize,
pub normalized_residual_mean: f64,
pub drift_mean: f64,
pub slew_mean: f64,
pub envelope_separation_mean: f64,
}
#[derive(Debug, Clone, Serialize)]
pub struct GroupGrammarStateRecord {
pub group_name: String,
pub run_index: usize,
pub timestamp: String,
pub label: i8,
pub active_feature_count: usize,
pub grammar_state: String,
pub boundary_member_count: usize,
pub pressure_member_count: usize,
pub violation_member_count: usize,
pub envelope_separation_mean: f64,
}
#[derive(Debug, Clone, Serialize)]
pub struct GroupSemanticMatchRecord {
pub group_name: String,
pub run_index: usize,
pub timestamp: String,
pub label: i8,
pub grammar_state: String,
pub heuristic_name: String,
pub participating_features: String,
pub structural_score_proxy: f64,
pub rank: usize,
}
#[derive(Debug, Clone, Serialize)]
pub struct GroupDefinitionRecord {
pub group_name: String,
pub member_features: String,
pub member_roles: String,
pub preferred_motifs: String,
pub empirical_basis: String,
pub group_size: usize,
pub rescue_eligible_member_count: usize,
pub highest_rescue_priority: String,
pub semantic_match_count: usize,
pub dominant_group_heuristic: Option<String>,
pub pressure_run_count: usize,
pub violation_run_count: usize,
pub mean_active_feature_count: f64,
pub mean_envelope_separation: f64,
pub coactivation_member_threshold: usize,
pub minimum_failure_coactivation_runs: usize,
pub failure_coactivation_run_count: usize,
pub pass_coactivation_run_count: usize,
pub validated: bool,
pub rejection_reason: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct ScaffoldSemioticsArtifacts {
pub feature_signs: Vec<FeatureSignRecord>,
pub feature_motif_timeline: Vec<FeatureMotifTimelineRecord>,
pub feature_grammar_states: Vec<FeatureGrammarStateRecord>,
pub envelope_interaction_summary: Vec<EnvelopeInteractionSummaryRow>,
pub heuristics_bank_expanded: Vec<ExpandedHeuristicEntry>,
pub feature_policy_decisions: Vec<FeaturePolicyDecisionRecord>,
pub group_definitions: Vec<GroupDefinitionRecord>,
pub group_signs: Vec<GroupSignRecord>,
pub group_grammar_states: Vec<GroupGrammarStateRecord>,
pub group_semantic_matches: Vec<GroupSemanticMatchRecord>,
}
#[derive(Debug, Clone, Copy)]
struct FeatureScaffoldSpec {
feature_name: &'static str,
role: &'static str,
preferred_motifs: &'static [&'static str],
default_policy_ceiling: HeuristicAlertClass,
rescue_eligible: bool,
rescue_priority: &'static str,
group_name: &'static str,
}
#[derive(Debug, Clone, Copy)]
struct GroupScaffoldSpec {
group_name: &'static str,
members: &'static [&'static str],
}
const FEATURE_SCAFFOLD: &[FeatureScaffoldSpec] = &[
FeatureScaffoldSpec {
feature_name: "S059",
role: "primary recurrent-boundary precursor",
preferred_motifs: &[RECURRENT_BOUNDARY_APPROACH, PRE_FAILURE_SLOW_DRIFT],
default_policy_ceiling: HeuristicAlertClass::Review,
rescue_eligible: true,
rescue_priority: "high",
group_name: "group_a",
},
FeatureScaffoldSpec {
feature_name: "S133",
role: "candidate slow-drift precursor",
preferred_motifs: &["slow_drift_precursor"],
default_policy_ceiling: HeuristicAlertClass::Review,
rescue_eligible: true,
rescue_priority: "high",
group_name: "group_a",
},
FeatureScaffoldSpec {
feature_name: "S123",
role: "transition / instability feature",
preferred_motifs: &[
TRANSITION_EXCURSION,
"persistent_instability",
"burst_instability",
],
default_policy_ceiling: HeuristicAlertClass::Escalate,
rescue_eligible: true,
rescue_priority: "medium",
group_name: "group_b",
},
FeatureScaffoldSpec {
feature_name: "S540",
role: "burst-support corroborator",
preferred_motifs: &[TRANSITION_CLUSTER_SUPPORT, "burst_instability"],
default_policy_ceiling: HeuristicAlertClass::Review,
rescue_eligible: false,
rescue_priority: "low",
group_name: "group_b",
},
FeatureScaffoldSpec {
feature_name: "S128",
role: "co-burst corroborator",
preferred_motifs: &[TRANSITION_CLUSTER_SUPPORT],
default_policy_ceiling: HeuristicAlertClass::Review,
rescue_eligible: false,
rescue_priority: "low",
group_name: "group_b",
},
FeatureScaffoldSpec {
feature_name: "S104",
role: "watch-only sentinel",
preferred_motifs: &[WATCH_ONLY_BOUNDARY_GRAZING, "noise_like"],
default_policy_ceiling: HeuristicAlertClass::Watch,
rescue_eligible: false,
rescue_priority: "none",
group_name: "group_c",
},
FeatureScaffoldSpec {
feature_name: "S134",
role: "recall-rescue feature",
preferred_motifs: &["recovery_pattern"],
default_policy_ceiling: HeuristicAlertClass::Review,
rescue_eligible: true,
rescue_priority: "high",
group_name: "ungrouped",
},
FeatureScaffoldSpec {
feature_name: "S275",
role: "recall-rescue feature",
preferred_motifs: &["recovery_pattern"],
default_policy_ceiling: HeuristicAlertClass::Review,
rescue_eligible: true,
rescue_priority: "high",
group_name: "ungrouped",
},
];
const GROUP_SCAFFOLD: &[GroupScaffoldSpec] = &[
GroupScaffoldSpec {
group_name: "group_a",
members: &["S059", "S133"],
},
GroupScaffoldSpec {
group_name: "group_b",
members: &["S123", "S540", "S128"],
},
GroupScaffoldSpec {
group_name: "group_c",
members: &["S104"],
},
];
const GROUP_FAILURE_COACTIVATION_MIN: usize = 2;
const GROUP_MEMBER_COACTIVATION_MIN: usize = 2;
pub fn classify_motifs(
dataset: &PreparedDataset,
nominal: &NominalModel,
residuals: &ResidualSet,
signs: &SignSet,
grammar: &GrammarSet,
pre_failure_lookback_runs: usize,
) -> MotifSet {
let failure_window_mask = failure_window_mask(
dataset.labels.len(),
&dataset.labels,
pre_failure_lookback_runs,
);
let mut traces = Vec::with_capacity(residuals.traces.len());
let mut counts = BTreeMap::<DsfbMotifClass, (usize, usize)>::new();
for (((residual_trace, sign_trace), grammar_trace), feature) in residuals
.traces
.iter()
.zip(&signs.traces)
.zip(&grammar.traces)
.zip(&nominal.features)
{
let labels =
classify_feature_motif_labels(residual_trace, sign_trace, grammar_trace, feature.rho);
for (run_index, label) in labels.iter().copied().enumerate() {
let entry = counts.entry(label).or_insert((0, 0));
entry.0 += 1;
if failure_window_mask[run_index] {
entry.1 += 1;
}
}
traces.push(FeatureMotifTrace {
feature_index: residual_trace.feature_index,
feature_name: residual_trace.feature_name.clone(),
labels,
});
}
let summary_rows = [
DsfbMotifClass::StableAdmissible,
DsfbMotifClass::RecurrentBoundaryApproach,
DsfbMotifClass::PreFailureSlowDrift,
DsfbMotifClass::TransitionExcursion,
DsfbMotifClass::PersistentInstabilityCluster,
DsfbMotifClass::TransitionClusterSupport,
DsfbMotifClass::WatchOnlyBoundaryGrazing,
]
.into_iter()
.map(|label| {
let (point_hits, pre_failure_point_hits) = counts.get(&label).copied().unwrap_or((0, 0));
MotifSummaryRow {
motif_label: label.as_lowercase().into(),
definition: label.definition().into(),
point_hits,
pre_failure_point_hits,
}
})
.collect();
MotifSet {
traces,
summary_rows,
}
}
pub fn build_semantic_layer(
dataset: &PreparedDataset,
residuals: &ResidualSet,
signs: &SignSet,
grammar: &GrammarSet,
motifs: &MotifSet,
nominal: &NominalModel,
pre_failure_lookback_runs: usize,
) -> SemanticLayer {
let failure_window_mask = failure_window_mask(
dataset.labels.len(),
&dataset.labels,
pre_failure_lookback_runs,
);
let mut semantic_matches = Vec::new();
let mut ranked_candidates = Vec::new();
for ((((residual_trace, sign_trace), grammar_trace), motif_trace), feature) in residuals
.traces
.iter()
.zip(&signs.traces)
.zip(&grammar.traces)
.zip(&motifs.traces)
.zip(&nominal.features)
{
let matches = build_feature_semantic_matches(
dataset,
residual_trace,
sign_trace,
grammar_trace,
motif_trace,
feature.rho,
);
ranked_candidates.extend(matches.iter().cloned());
semantic_matches.extend(matches);
}
let structural_delta_metrics = compute_structural_delta_metrics(
residuals,
grammar,
&semantic_matches,
&failure_window_mask,
);
SemanticLayer {
semantic_matches,
ranked_candidates,
structural_delta_metrics,
}
}
pub fn feature_semantic_flags(
residual_trace: &ResidualFeatureTrace,
_sign_trace: &FeatureSigns,
grammar_trace: &FeatureGrammarTrace,
_feature_rho: f64,
) -> FeatureSemanticFlags {
let mut semantic_flags = BTreeMap::<&'static str, Vec<bool>>::new();
for heuristic_name in [
PRE_FAILURE_SLOW_DRIFT,
TRANSIENT_EXCURSION,
RECURRENT_BOUNDARY_APPROACH,
] {
semantic_flags.insert(heuristic_name, vec![false; residual_trace.norms.len()]);
}
let mut any_semantic_match = vec![false; residual_trace.norms.len()];
for run_index in 0..residual_trace.norms.len() {
let state = grammar_trace.raw_states[run_index];
let reason = grammar_trace.raw_reasons[run_index];
if state == GrammarState::Boundary && reason == GrammarReason::SustainedOutwardDrift {
semantic_flags
.get_mut(PRE_FAILURE_SLOW_DRIFT)
.expect("pre_failure_slow_drift bucket")[run_index] = true;
any_semantic_match[run_index] = true;
}
if matches!(state, GrammarState::Boundary | GrammarState::Violation)
&& reason == GrammarReason::AbruptSlewViolation
{
semantic_flags
.get_mut(TRANSIENT_EXCURSION)
.expect("transient_excursion bucket")[run_index] = true;
any_semantic_match[run_index] = true;
}
if state == GrammarState::Boundary && reason == GrammarReason::RecurrentBoundaryGrazing {
semantic_flags
.get_mut(RECURRENT_BOUNDARY_APPROACH)
.expect("recurrent_boundary_approach bucket")[run_index] = true;
any_semantic_match[run_index] = true;
}
}
FeatureSemanticFlags {
semantic_flags,
any_semantic_match,
}
}
pub fn build_scaffold_semiotics(
dataset: &PreparedDataset,
nominal: &NominalModel,
residuals: &ResidualSet,
grammar: &GrammarSet,
motifs: &MotifSet,
semantic_layer: &SemanticLayer,
) -> ScaffoldSemioticsArtifacts {
let selected = FEATURE_SCAFFOLD
.iter()
.filter_map(|spec| {
let feature = nominal
.features
.iter()
.find(|feature| feature.feature_name == spec.feature_name)?;
let residual_trace = residuals
.traces
.iter()
.find(|trace| trace.feature_index == feature.feature_index)?;
let grammar_trace = grammar
.traces
.iter()
.find(|trace| trace.feature_index == feature.feature_index)?;
let motif_trace = motifs
.traces
.iter()
.find(|trace| trace.feature_index == feature.feature_index)?;
Some((
spec,
feature.rho,
residual_trace,
grammar_trace,
motif_trace,
))
})
.collect::<Vec<_>>();
let feature_signs = build_feature_sign_records(dataset, &selected);
let feature_motif_timeline = build_feature_motif_timeline(dataset, &selected);
let feature_grammar_states = build_feature_grammar_states(dataset, &selected);
let envelope_interaction_summary = build_envelope_interaction_summary(&selected);
let heuristics_bank_expanded = build_expanded_heuristics_bank();
let candidate_group_signs = build_group_signs(dataset, &feature_signs);
let candidate_group_grammar_states =
build_group_grammar_states(dataset, &candidate_group_signs, &feature_grammar_states);
let candidate_group_semantic_matches =
build_group_semantic_matches(dataset, &candidate_group_grammar_states, semantic_layer);
let group_definitions = build_group_definitions(
&candidate_group_signs,
&candidate_group_grammar_states,
&candidate_group_semantic_matches,
);
let valid_group_names = group_definitions
.iter()
.filter(|row| row.validated)
.map(|row| row.group_name.as_str())
.collect::<BTreeSet<_>>();
let group_signs = candidate_group_signs
.into_iter()
.filter(|row| valid_group_names.contains(row.group_name.as_str()))
.collect::<Vec<_>>();
let group_grammar_states = candidate_group_grammar_states
.into_iter()
.filter(|row| valid_group_names.contains(row.group_name.as_str()))
.collect::<Vec<_>>();
let group_semantic_matches = candidate_group_semantic_matches
.into_iter()
.filter(|row| valid_group_names.contains(row.group_name.as_str()))
.collect::<Vec<_>>();
let feature_policy_decisions =
build_feature_policy_decisions(dataset, &selected, semantic_layer, &group_semantic_matches);
ScaffoldSemioticsArtifacts {
feature_signs,
feature_motif_timeline,
feature_grammar_states,
envelope_interaction_summary,
heuristics_bank_expanded,
feature_policy_decisions,
group_definitions,
group_signs,
group_grammar_states,
group_semantic_matches,
}
}
fn classify_feature_motif_labels(
residual_trace: &ResidualFeatureTrace,
sign_trace: &FeatureSigns,
grammar_trace: &FeatureGrammarTrace,
feature_rho: f64,
) -> Vec<DsfbMotifClass> {
let mut labels = Vec::with_capacity(residual_trace.norms.len());
for run_index in 0..residual_trace.norms.len() {
let grammar_label = classify_grammar_label(grammar_trace, run_index);
let norm_ratio = residual_trace.norms[run_index] / feature_rho.max(1.0e-12);
let drift_threshold = sign_trace.drift_threshold.max(1.0e-12);
let slew_threshold = sign_trace.slew_threshold.max(1.0e-12);
let drift_ratio = sign_trace.drift[run_index].abs() / drift_threshold;
let slew_ratio = sign_trace.slew[run_index].abs() / slew_threshold;
let recent_start = run_index.saturating_sub(4);
let recent_non_admissible = (recent_start..=run_index)
.filter(|&index| {
classify_grammar_label(grammar_trace, index) != ScaffoldGrammarState::Admissible
})
.count();
let recent_pressure = (recent_start..=run_index)
.filter(|&index| {
matches!(
classify_grammar_label(grammar_trace, index),
ScaffoldGrammarState::BoundaryGrazing
| ScaffoldGrammarState::SustainedOutwardDrift
| ScaffoldGrammarState::TransientViolation
| ScaffoldGrammarState::PersistentViolation
)
})
.count();
let is_corroborator = matches!(residual_trace.feature_name.as_str(), "S540" | "S128");
let is_sentinel = residual_trace.feature_name == "S104";
let label = if grammar_label == ScaffoldGrammarState::PersistentViolation
|| (recent_pressure >= 3
&& matches!(grammar_label, ScaffoldGrammarState::SustainedOutwardDrift))
{
if is_corroborator {
DsfbMotifClass::TransitionClusterSupport
} else {
DsfbMotifClass::PersistentInstabilityCluster
}
} else if matches!(grammar_label, ScaffoldGrammarState::TransientViolation)
&& slew_ratio >= 1.0
{
if is_corroborator {
DsfbMotifClass::TransitionClusterSupport
} else {
DsfbMotifClass::TransitionExcursion
}
} else if grammar_label == ScaffoldGrammarState::SustainedOutwardDrift
&& sign_trace.drift[run_index] >= sign_trace.drift_threshold
&& slew_ratio < 1.0
&& norm_ratio >= 0.40
{
DsfbMotifClass::PreFailureSlowDrift
} else if grammar_label == ScaffoldGrammarState::BoundaryGrazing {
if is_sentinel || recent_non_admissible < 2 {
DsfbMotifClass::WatchOnlyBoundaryGrazing
} else {
DsfbMotifClass::RecurrentBoundaryApproach
}
} else if is_corroborator && recent_pressure >= 2 {
DsfbMotifClass::TransitionClusterSupport
} else if norm_ratio <= 0.25
&& drift_ratio < 1.0
&& slew_ratio < 1.0
&& matches!(
grammar_label,
ScaffoldGrammarState::Admissible | ScaffoldGrammarState::Recovery
)
{
DsfbMotifClass::StableAdmissible
} else if recent_non_admissible >= 2 {
DsfbMotifClass::RecurrentBoundaryApproach
} else {
DsfbMotifClass::StableAdmissible
};
labels.push(label);
}
labels
}
fn build_feature_semantic_matches(
dataset: &PreparedDataset,
residual_trace: &ResidualFeatureTrace,
sign_trace: &FeatureSigns,
grammar_trace: &FeatureGrammarTrace,
motif_trace: &FeatureMotifTrace,
feature_rho: f64,
) -> Vec<SemanticMatchRecord> {
let mut rows = Vec::new();
let feature_role = feature_scaffold_spec(&residual_trace.feature_name)
.map(|spec| spec.role)
.unwrap_or("unscaffolded_feature");
for run_index in 0..motif_trace.labels.len() {
let grammar_label = classify_grammar_label(grammar_trace, run_index);
let candidates = semantic_candidates_for_run(
&residual_trace.feature_name,
grammar_label,
grammar_trace.raw_reasons[run_index],
motif_trace.labels[run_index],
);
for (rank, heuristic_name) in candidates.into_iter().enumerate() {
let Some(policy) = heuristic_policy_definition(heuristic_name) else {
continue;
};
let metadata = expanded_heuristic_entry_metadata(heuristic_name);
rows.push(SemanticMatchRecord {
feature_index: residual_trace.feature_index,
feature_name: residual_trace.feature_name.clone(),
feature_role: feature_role.into(),
run_index,
timestamp: dataset.timestamps[run_index]
.format("%Y-%m-%d %H:%M:%S")
.to_string(),
label: dataset.labels[run_index],
grammar_state: grammar_label.as_lowercase().into(),
grammar_reason: format!("{:?}", grammar_trace.raw_reasons[run_index]),
motif_label: motif_trace.labels[run_index].as_lowercase().into(),
heuristic_name: heuristic_name.into(),
alert_class_default: policy.alert_class_default.as_lowercase().into(),
grammar_constraints: metadata.allowed_grammar_states.into(),
regime_conditions: semantic_regime_conditions(heuristic_name).into(),
applicability_rules: semantic_applicability_rules(heuristic_name).into(),
feature_scope: metadata.feature_scope.into(),
ambiguity_note: metadata.ambiguity_note.into(),
rescue_eligibility_guidance: metadata.rescue_eligibility_guidance.into(),
burden_contribution_class: metadata.burden_contribution_class.into(),
structural_score_proxy: sign_trace.drift[run_index].abs()
+ sign_trace.slew[run_index].abs()
+ (residual_trace.norms[run_index] / feature_rho.max(1.0e-12)),
rank: rank + 1,
});
}
}
rows
}
fn semantic_candidates_for_run(
feature_name: &str,
grammar_state: ScaffoldGrammarState,
grammar_reason: GrammarReason,
motif_label: DsfbMotifClass,
) -> Vec<&'static str> {
let mut candidates = Vec::new();
let is_corroborator = matches!(feature_name, "S540" | "S128");
if grammar_state == ScaffoldGrammarState::SustainedOutwardDrift
&& motif_label == DsfbMotifClass::PreFailureSlowDrift
{
candidates.push(PRE_FAILURE_SLOW_DRIFT);
}
if matches!(
grammar_state,
ScaffoldGrammarState::TransientViolation | ScaffoldGrammarState::PersistentViolation
) && motif_label == DsfbMotifClass::TransitionExcursion
{
candidates.push(TRANSITION_EXCURSION);
}
if grammar_state == ScaffoldGrammarState::BoundaryGrazing
&& motif_label == DsfbMotifClass::RecurrentBoundaryApproach
{
candidates.push(RECURRENT_BOUNDARY_APPROACH);
}
if matches!(
grammar_state,
ScaffoldGrammarState::SustainedOutwardDrift | ScaffoldGrammarState::PersistentViolation
) && motif_label == DsfbMotifClass::PersistentInstabilityCluster
{
candidates.push(PERSISTENT_INSTABILITY_CLUSTER);
}
if is_corroborator
&& matches!(
grammar_state,
ScaffoldGrammarState::BoundaryGrazing
| ScaffoldGrammarState::SustainedOutwardDrift
| ScaffoldGrammarState::TransientViolation
)
&& motif_label == DsfbMotifClass::TransitionClusterSupport
{
candidates.push(TRANSITION_CLUSTER_SUPPORT);
}
if grammar_state == ScaffoldGrammarState::BoundaryGrazing
&& motif_label == DsfbMotifClass::WatchOnlyBoundaryGrazing
{
candidates.push(WATCH_ONLY_BOUNDARY_GRAZING);
}
if grammar_reason == GrammarReason::AbruptSlewViolation
&& candidates.is_empty()
&& !is_corroborator
{
candidates.push(TRANSITION_EXCURSION);
}
candidates
}
fn build_feature_sign_records(
dataset: &PreparedDataset,
selected: &[(
&FeatureScaffoldSpec,
f64,
&ResidualFeatureTrace,
&FeatureGrammarTrace,
&FeatureMotifTrace,
)],
) -> Vec<FeatureSignRecord> {
let mut rows = Vec::new();
for (spec, rho, residual_trace, _, _) in selected {
let mut normalized_residual = Vec::with_capacity(residual_trace.residuals.len());
let mut drift = vec![0.0; residual_trace.residuals.len()];
let mut slew = vec![0.0; residual_trace.residuals.len()];
for run_index in 0..residual_trace.residuals.len() {
normalized_residual.push(residual_trace.residuals[run_index] / rho.max(1.0e-12));
}
for run_index in 1..normalized_residual.len() {
if residual_trace.is_imputed[run_index] || residual_trace.is_imputed[run_index - 1] {
drift[run_index] = 0.0;
} else {
drift[run_index] =
normalized_residual[run_index] - normalized_residual[run_index - 1];
}
}
for run_index in 1..drift.len() {
if residual_trace.is_imputed[run_index] || residual_trace.is_imputed[run_index - 1] {
slew[run_index] = 0.0;
} else {
slew[run_index] = drift[run_index] - drift[run_index - 1];
}
}
for run_index in 0..normalized_residual.len() {
rows.push(FeatureSignRecord {
feature_index: residual_trace.feature_index,
feature_name: residual_trace.feature_name.clone(),
feature_role: spec.role.into(),
group_name: spec.group_name.into(),
run_index,
timestamp: dataset.timestamps[run_index]
.format("%Y-%m-%d %H:%M:%S")
.to_string(),
label: dataset.labels[run_index],
normalized_residual: normalized_residual[run_index],
drift: drift[run_index],
slew: slew[run_index],
normalized_residual_norm: residual_trace.norms[run_index] / rho.max(1.0e-12),
sigma_norm: (normalized_residual[run_index] * normalized_residual[run_index]
+ drift[run_index] * drift[run_index]
+ slew[run_index] * slew[run_index])
.sqrt(),
is_imputed: residual_trace.is_imputed[run_index],
});
}
}
rows
}
fn build_feature_motif_timeline(
dataset: &PreparedDataset,
selected: &[(
&FeatureScaffoldSpec,
f64,
&ResidualFeatureTrace,
&FeatureGrammarTrace,
&FeatureMotifTrace,
)],
) -> Vec<FeatureMotifTimelineRecord> {
let mut rows = Vec::new();
for (spec, _, residual_trace, _, motif_trace) in selected {
for (run_index, motif_label) in motif_trace.labels.iter().enumerate() {
rows.push(FeatureMotifTimelineRecord {
feature_index: residual_trace.feature_index,
feature_name: residual_trace.feature_name.clone(),
feature_role: spec.role.into(),
group_name: spec.group_name.into(),
run_index,
timestamp: dataset.timestamps[run_index]
.format("%Y-%m-%d %H:%M:%S")
.to_string(),
label: dataset.labels[run_index],
motif_label: motif_label.as_lowercase().into(),
});
}
}
rows
}
fn build_feature_grammar_states(
dataset: &PreparedDataset,
selected: &[(
&FeatureScaffoldSpec,
f64,
&ResidualFeatureTrace,
&FeatureGrammarTrace,
&FeatureMotifTrace,
)],
) -> Vec<FeatureGrammarStateRecord> {
let mut rows = Vec::new();
for (spec, rho, residual_trace, grammar_trace, _) in selected {
for run_index in 0..grammar_trace.raw_states.len() {
rows.push(FeatureGrammarStateRecord {
feature_index: residual_trace.feature_index,
feature_name: residual_trace.feature_name.clone(),
feature_role: spec.role.into(),
group_name: spec.group_name.into(),
run_index,
timestamp: dataset.timestamps[run_index]
.format("%Y-%m-%d %H:%M:%S")
.to_string(),
label: dataset.labels[run_index],
grammar_state: classify_grammar_label(grammar_trace, run_index)
.as_lowercase()
.into(),
raw_state: format!("{:?}", grammar_trace.raw_states[run_index]),
confirmed_state: format!("{:?}", grammar_trace.states[run_index]),
raw_reason: format!("{:?}", grammar_trace.raw_reasons[run_index]),
confirmed_reason: format!("{:?}", grammar_trace.reasons[run_index]),
normalized_envelope_ratio: residual_trace.norms[run_index] / rho.max(1.0e-12),
persistent_boundary: grammar_trace.persistent_boundary[run_index],
persistent_violation: grammar_trace.persistent_violation[run_index],
suppressed_by_imputation: grammar_trace.suppressed_by_imputation[run_index],
});
}
}
rows
}
fn build_envelope_interaction_summary(
selected: &[(
&FeatureScaffoldSpec,
f64,
&ResidualFeatureTrace,
&FeatureGrammarTrace,
&FeatureMotifTrace,
)],
) -> Vec<EnvelopeInteractionSummaryRow> {
selected
.iter()
.map(|(spec, rho, residual_trace, grammar_trace, _)| {
let mut boundary_grazing_points = 0usize;
let mut sustained_outward_drift_points = 0usize;
let mut transient_violation_points = 0usize;
let mut persistent_violation_points = 0usize;
let mut recovery_points = 0usize;
let mut max_ratio = 0.0_f64;
let mut ratios = Vec::with_capacity(residual_trace.norms.len());
for run_index in 0..residual_trace.norms.len() {
let grammar_label = classify_grammar_label(grammar_trace, run_index);
match grammar_label {
ScaffoldGrammarState::BoundaryGrazing => boundary_grazing_points += 1,
ScaffoldGrammarState::SustainedOutwardDrift => {
sustained_outward_drift_points += 1
}
ScaffoldGrammarState::TransientViolation => transient_violation_points += 1,
ScaffoldGrammarState::PersistentViolation => persistent_violation_points += 1,
ScaffoldGrammarState::Recovery => recovery_points += 1,
ScaffoldGrammarState::Admissible => {}
}
let ratio = residual_trace.norms[run_index] / rho.max(1.0e-12);
max_ratio = max_ratio.max(ratio);
ratios.push(ratio);
}
EnvelopeInteractionSummaryRow {
feature_index: residual_trace.feature_index,
feature_name: residual_trace.feature_name.clone(),
feature_role: spec.role.into(),
group_name: spec.group_name.into(),
boundary_grazing_points,
sustained_outward_drift_points,
transient_violation_points,
persistent_violation_points,
recovery_points,
max_normalized_envelope_ratio: max_ratio,
mean_normalized_envelope_ratio: mean(&ratios).unwrap_or(0.0),
}
})
.collect()
}
fn build_expanded_heuristics_bank() -> Vec<ExpandedHeuristicEntry> {
expanded_semantic_policy_definitions()
.into_iter()
.map(|definition| {
let metadata = expanded_heuristic_entry_metadata(definition.motif_name);
ExpandedHeuristicEntry {
heuristic_name: definition.motif_name.into(),
motif_signature: definition.signature_definition.into(),
allowed_grammar_states: metadata.allowed_grammar_states.into(),
role_class: metadata.role_class.into(),
feature_scope: metadata.feature_scope.into(),
interpretation_text: definition.interpretation.into(),
ambiguity_note: metadata.ambiguity_note.into(),
rescue_eligibility_guidance: metadata.rescue_eligibility_guidance.into(),
burden_contribution_class: metadata.burden_contribution_class.into(),
alert_class_default: definition.alert_class_default.as_lowercase().into(),
requires_persistence: definition.requires_persistence,
requires_corroboration: definition.requires_corroboration,
minimum_window: definition.minimum_window,
minimum_hits: definition.minimum_hits,
maximum_allowed_fragmentation: definition.maximum_allowed_fragmentation(),
}
})
.collect()
}
fn build_group_signs(
dataset: &PreparedDataset,
feature_signs: &[FeatureSignRecord],
) -> Vec<GroupSignRecord> {
let by_key = feature_signs.iter().fold(
BTreeMap::<(&str, usize), Vec<&FeatureSignRecord>>::new(),
|mut acc, row| {
acc.entry((row.group_name.as_str(), row.run_index))
.or_default()
.push(row);
acc
},
);
let mut rows: Vec<GroupSignRecord> = Vec::new();
for group in GROUP_SCAFFOLD {
for run_index in 0..dataset.labels.len() {
let members = by_key
.get(&(group.group_name, run_index))
.cloned()
.unwrap_or_default();
let active_feature_count = members.len();
let normalized_residual_mean = mean_of_records(&members, |row| row.normalized_residual);
let drift_mean = mean_of_records(&members, |row| row.drift);
let slew_mean = mean_of_records(&members, |row| row.slew);
let envelope_separation_mean =
mean_of_records(&members, |row| row.normalized_residual_norm);
rows.push(GroupSignRecord {
group_name: group.group_name.into(),
run_index,
timestamp: dataset.timestamps[run_index]
.format("%Y-%m-%d %H:%M:%S")
.to_string(),
label: dataset.labels[run_index],
active_feature_count,
normalized_residual_mean,
drift_mean,
slew_mean,
envelope_separation_mean,
});
}
}
rows
}
fn build_group_grammar_states(
dataset: &PreparedDataset,
group_signs: &[GroupSignRecord],
feature_grammar_states: &[FeatureGrammarStateRecord],
) -> Vec<GroupGrammarStateRecord> {
let feature_by_key = feature_grammar_states.iter().fold(
BTreeMap::<(&str, usize), Vec<&FeatureGrammarStateRecord>>::new(),
|mut acc, row| {
acc.entry((row.group_name.as_str(), row.run_index))
.or_default()
.push(row);
acc
},
);
let mut rows: Vec<GroupGrammarStateRecord> = Vec::new();
for sign_row in group_signs {
let members = feature_by_key
.get(&(sign_row.group_name.as_str(), sign_row.run_index))
.cloned()
.unwrap_or_default();
let boundary_member_count = members
.iter()
.filter(|row| row.grammar_state == ScaffoldGrammarState::BoundaryGrazing.as_lowercase())
.count();
let pressure_member_count = members
.iter()
.filter(|row| {
matches!(
row.grammar_state.as_str(),
"boundary_grazing"
| "sustained_outward_drift"
| "transient_violation"
| "persistent_violation"
)
})
.count();
let violation_member_count = members
.iter()
.filter(|row| {
matches!(
row.grammar_state.as_str(),
"transient_violation" | "persistent_violation"
)
})
.count();
let previous_state = sign_row.run_index.checked_sub(1).and_then(|previous| {
rows.iter()
.rev()
.find(|row| row.group_name == sign_row.group_name && row.run_index == previous)
.map(|row| row.grammar_state.as_str())
});
let grammar_state = if sign_row.active_feature_count == 0 {
ScaffoldGrammarState::Admissible
} else if sign_row.envelope_separation_mean >= 1.0 && violation_member_count >= 2 {
ScaffoldGrammarState::PersistentViolation
} else if sign_row.envelope_separation_mean >= 1.0 && violation_member_count >= 1 {
ScaffoldGrammarState::TransientViolation
} else if sign_row.envelope_separation_mean >= 0.6 && sign_row.drift_mean > 0.0 {
ScaffoldGrammarState::SustainedOutwardDrift
} else if sign_row.envelope_separation_mean >= 0.6 && boundary_member_count >= 1 {
ScaffoldGrammarState::BoundaryGrazing
} else if previous_state.is_some_and(|state| {
matches!(
state,
"boundary_grazing"
| "sustained_outward_drift"
| "transient_violation"
| "persistent_violation"
)
}) && sign_row.envelope_separation_mean < 0.4
{
ScaffoldGrammarState::Recovery
} else {
ScaffoldGrammarState::Admissible
};
rows.push(GroupGrammarStateRecord {
group_name: sign_row.group_name.clone(),
run_index: sign_row.run_index,
timestamp: dataset.timestamps[sign_row.run_index]
.format("%Y-%m-%d %H:%M:%S")
.to_string(),
label: dataset.labels[sign_row.run_index],
active_feature_count: sign_row.active_feature_count,
grammar_state: grammar_state.as_lowercase().into(),
boundary_member_count,
pressure_member_count,
violation_member_count,
envelope_separation_mean: sign_row.envelope_separation_mean,
});
}
rows
}
fn build_group_semantic_matches(
dataset: &PreparedDataset,
group_grammar_states: &[GroupGrammarStateRecord],
semantic_layer: &SemanticLayer,
) -> Vec<GroupSemanticMatchRecord> {
let feature_matches = semantic_layer.semantic_matches.iter().fold(
BTreeMap::<(&str, usize), Vec<&SemanticMatchRecord>>::new(),
|mut acc, row| {
if let Some(spec) = feature_scaffold_spec(&row.feature_name) {
acc.entry((spec.group_name, row.run_index))
.or_default()
.push(row);
}
acc
},
);
let mut rows = Vec::new();
for group_row in group_grammar_states {
let Some(group_spec) = group_scaffold_spec(&group_row.group_name) else {
continue;
};
let matches = feature_matches
.get(&(group_row.group_name.as_str(), group_row.run_index))
.cloned()
.unwrap_or_default();
let mut candidates = Vec::<(&'static str, Vec<String>, f64)>::new();
let participating_features = matches
.iter()
.map(|row| row.feature_name.clone())
.collect::<Vec<_>>();
let score = matches
.iter()
.map(|row| row.structural_score_proxy)
.fold(0.0, f64::max);
if group_row.group_name == "group_a" {
if matches
.iter()
.any(|row| row.heuristic_name == PRE_FAILURE_SLOW_DRIFT)
&& matches!(
group_row.grammar_state.as_str(),
"sustained_outward_drift" | "persistent_violation"
)
{
candidates.push((
PRE_FAILURE_SLOW_DRIFT,
participating_features.clone(),
score,
));
}
if matches
.iter()
.any(|row| row.heuristic_name == RECURRENT_BOUNDARY_APPROACH)
&& matches!(
group_row.grammar_state.as_str(),
"boundary_grazing" | "sustained_outward_drift"
)
{
candidates.push((
RECURRENT_BOUNDARY_APPROACH,
participating_features.clone(),
score,
));
}
} else if group_row.group_name == "group_b" {
if matches
.iter()
.any(|row| row.heuristic_name == TRANSITION_EXCURSION)
&& matches!(
group_row.grammar_state.as_str(),
"transient_violation" | "persistent_violation"
)
{
candidates.push((
PERSISTENT_INSTABILITY_CLUSTER,
participating_features.clone(),
score,
));
}
if matches
.iter()
.any(|row| row.heuristic_name == TRANSITION_CLUSTER_SUPPORT)
{
candidates.push((
TRANSITION_CLUSTER_SUPPORT,
participating_features.clone(),
score,
));
}
} else if group_row.group_name == "group_c"
&& group_row.grammar_state == ScaffoldGrammarState::BoundaryGrazing.as_lowercase()
&& matches
.iter()
.any(|row| row.heuristic_name == WATCH_ONLY_BOUNDARY_GRAZING)
{
candidates.push((
WATCH_ONLY_BOUNDARY_GRAZING,
participating_features.clone(),
score,
));
}
for (rank, (heuristic_name, participating, structural_score_proxy)) in
candidates.into_iter().enumerate()
{
rows.push(GroupSemanticMatchRecord {
group_name: group_spec.group_name.into(),
run_index: group_row.run_index,
timestamp: dataset.timestamps[group_row.run_index]
.format("%Y-%m-%d %H:%M:%S")
.to_string(),
label: dataset.labels[group_row.run_index],
grammar_state: group_row.grammar_state.clone(),
heuristic_name: heuristic_name.into(),
participating_features: participating.join(","),
structural_score_proxy,
rank: rank + 1,
});
}
}
rows
}
fn build_group_definitions(
group_signs: &[GroupSignRecord],
group_grammar_states: &[GroupGrammarStateRecord],
group_semantic_matches: &[GroupSemanticMatchRecord],
) -> Vec<GroupDefinitionRecord> {
let group_signs_by_name = group_signs.iter().fold(
BTreeMap::<&str, Vec<&GroupSignRecord>>::new(),
|mut acc, row| {
acc.entry(row.group_name.as_str()).or_default().push(row);
acc
},
);
let group_grammar_by_name = group_grammar_states.iter().fold(
BTreeMap::<&str, Vec<&GroupGrammarStateRecord>>::new(),
|mut acc, row| {
acc.entry(row.group_name.as_str()).or_default().push(row);
acc
},
);
let group_semantics_by_name = group_semantic_matches.iter().fold(
BTreeMap::<&str, Vec<&GroupSemanticMatchRecord>>::new(),
|mut acc, row| {
acc.entry(row.group_name.as_str()).or_default().push(row);
acc
},
);
GROUP_SCAFFOLD
.iter()
.map(|group| {
let signs = group_signs_by_name
.get(group.group_name)
.cloned()
.unwrap_or_default();
let grammar_rows = group_grammar_by_name
.get(group.group_name)
.cloned()
.unwrap_or_default();
let semantic_rows = group_semantics_by_name
.get(group.group_name)
.cloned()
.unwrap_or_default();
let members = group
.members
.iter()
.filter_map(|feature_name| feature_scaffold_spec(feature_name))
.collect::<Vec<_>>();
let preferred_motifs = members
.iter()
.flat_map(|spec| spec.preferred_motifs.iter().copied())
.collect::<Vec<_>>();
let mut preferred_motifs = preferred_motifs;
preferred_motifs.sort_unstable();
preferred_motifs.dedup();
let mut heuristic_counts = BTreeMap::<String, usize>::new();
for row in &semantic_rows {
*heuristic_counts
.entry(row.heuristic_name.clone())
.or_default() += 1;
}
let dominant_group_heuristic = heuristic_counts
.into_iter()
.max_by(|left, right| left.1.cmp(&right.1).then_with(|| right.0.cmp(&left.0)))
.map(|(name, _)| name);
let highest_rescue_priority = members
.iter()
.map(|spec| spec.rescue_priority)
.max_by_key(|priority| rescue_priority_rank(priority))
.unwrap_or("none");
let coactivation_member_threshold = group.members.len().min(GROUP_MEMBER_COACTIVATION_MIN).max(1);
let failure_coactivation_run_count = grammar_rows
.iter()
.filter(|row| {
row.label == 1
&& row.pressure_member_count >= coactivation_member_threshold
})
.count();
let pass_coactivation_run_count = grammar_rows
.iter()
.filter(|row| {
row.label == -1
&& row.pressure_member_count >= coactivation_member_threshold
})
.count();
let validated = failure_coactivation_run_count >= GROUP_FAILURE_COACTIVATION_MIN
&& pass_coactivation_run_count == 0;
let rejection_reason = if validated {
None
} else if failure_coactivation_run_count < GROUP_FAILURE_COACTIVATION_MIN {
Some("failure co-activation is below the required minimum".into())
} else if pass_coactivation_run_count > 0 {
Some("group co-activates in pass runs and is rejected".into())
} else {
Some("group failed strict grouped-semiotics validation".into())
};
GroupDefinitionRecord {
group_name: group.group_name.into(),
member_features: group.members.join(","),
member_roles: members
.iter()
.map(|spec| spec.role)
.collect::<Vec<_>>()
.join(","),
preferred_motifs: preferred_motifs.join(","),
empirical_basis: format!(
"Scaffolded from saved top-feature co-activity; {} grouped semantic matches, {} pressure runs, {} violation runs.",
semantic_rows.len(),
grammar_rows
.iter()
.filter(|row| {
matches!(
row.grammar_state.as_str(),
"boundary_grazing"
| "sustained_outward_drift"
| "transient_violation"
| "persistent_violation"
)
})
.count(),
grammar_rows
.iter()
.filter(|row| {
matches!(
row.grammar_state.as_str(),
"transient_violation" | "persistent_violation"
)
})
.count()
),
group_size: group.members.len(),
rescue_eligible_member_count: members
.iter()
.filter(|spec| spec.rescue_eligible)
.count(),
highest_rescue_priority: highest_rescue_priority.into(),
semantic_match_count: semantic_rows.len(),
dominant_group_heuristic,
pressure_run_count: grammar_rows
.iter()
.filter(|row| {
matches!(
row.grammar_state.as_str(),
"boundary_grazing"
| "sustained_outward_drift"
| "transient_violation"
| "persistent_violation"
)
})
.count(),
violation_run_count: grammar_rows
.iter()
.filter(|row| {
matches!(
row.grammar_state.as_str(),
"transient_violation" | "persistent_violation"
)
})
.count(),
mean_active_feature_count: mean_of_records(&signs, |row| row.active_feature_count as f64),
mean_envelope_separation: mean_of_records(&signs, |row| row.envelope_separation_mean),
coactivation_member_threshold,
minimum_failure_coactivation_runs: GROUP_FAILURE_COACTIVATION_MIN,
failure_coactivation_run_count,
pass_coactivation_run_count,
validated,
rejection_reason,
}
})
.collect()
}
fn build_feature_policy_decisions(
dataset: &PreparedDataset,
selected: &[(
&FeatureScaffoldSpec,
f64,
&ResidualFeatureTrace,
&FeatureGrammarTrace,
&FeatureMotifTrace,
)],
semantic_layer: &SemanticLayer,
group_semantic_matches: &[GroupSemanticMatchRecord],
) -> Vec<FeaturePolicyDecisionRecord> {
let semantic_by_feature_run = semantic_layer
.ranked_candidates
.iter()
.filter_map(|row| {
feature_scaffold_spec(&row.feature_name)?;
Some(((row.feature_name.clone(), row.run_index), row.clone()))
})
.fold(
BTreeMap::<(String, usize), SemanticMatchRecord>::new(),
|mut acc, (key, row)| {
acc.entry(key)
.and_modify(|existing| {
if row.rank < existing.rank {
*existing = row.clone();
}
})
.or_insert(row);
acc
},
);
let group_support = group_semantic_matches.iter().fold(
BTreeMap::<(&str, usize), Vec<&GroupSemanticMatchRecord>>::new(),
|mut acc, row| {
acc.entry((row.group_name.as_str(), row.run_index))
.or_default()
.push(row);
acc
},
);
let mut rows = Vec::new();
let mut base_states = BTreeMap::<(String, usize), String>::new();
for (spec, _, residual_trace, grammar_trace, motif_trace) in selected {
for run_index in 0..residual_trace.norms.len() {
let semantic = semantic_by_feature_run
.get(&(residual_trace.feature_name.clone(), run_index))
.cloned();
let grammar_state = classify_grammar_label(grammar_trace, run_index);
let motif_label = motif_trace.labels[run_index];
let group_matches = group_support
.get(&(spec.group_name, run_index))
.cloned()
.unwrap_or_default();
let mut corroborators = group_matches
.iter()
.flat_map(|row| row.participating_features.split(','))
.map(str::trim)
.filter(|name| !name.is_empty() && *name != residual_trace.feature_name)
.map(str::to_string)
.collect::<Vec<_>>();
corroborators.sort();
corroborators.dedup();
let (policy_state, rationale) = base_policy_state(
spec,
grammar_state,
motif_label,
semantic.as_ref(),
!corroborators.is_empty(),
);
base_states.insert(
(residual_trace.feature_name.clone(), run_index),
policy_state.to_string(),
);
rows.push(FeaturePolicyDecisionRecord {
feature_index: residual_trace.feature_index,
feature_name: residual_trace.feature_name.clone(),
feature_role: spec.role.into(),
group_name: spec.group_name.into(),
run_index,
timestamp: dataset.timestamps[run_index]
.format("%Y-%m-%d %H:%M:%S")
.to_string(),
label: dataset.labels[run_index],
grammar_state: grammar_state.as_lowercase().into(),
motif_label: motif_label.as_lowercase().into(),
semantic_label: semantic.as_ref().map(|row| row.heuristic_name.clone()),
policy_ceiling: spec.default_policy_ceiling.as_lowercase().into(),
policy_state: policy_state.into(),
investigation_worthy: matches!(policy_state, "review" | "escalate"),
corroborated: !corroborators.is_empty(),
corroborated_by: corroborators.join(","),
rationale,
});
}
}
for row in &mut rows {
let related_support = related_feature_support(
&base_states,
&row.feature_name,
row.run_index,
row.corroborated,
);
if row.feature_name == "S059"
&& row.policy_state == "review"
&& (related_support
|| row.grammar_state == ScaffoldGrammarState::PersistentViolation.as_lowercase())
{
row.policy_state = "escalate".into();
row.investigation_worthy = true;
row.rationale =
"primary precursor escalated only after corroboration or persistent violation"
.into();
} else if row.feature_name == "S123"
&& row.policy_state == "review"
&& (related_support
|| row.grammar_state == ScaffoldGrammarState::PersistentViolation.as_lowercase())
{
row.policy_state = "escalate".into();
row.investigation_worthy = true;
row.rationale = "transition instability escalated under grammar transition support or persistent violation".into();
} else if matches!(row.feature_name.as_str(), "S540" | "S128")
&& row.policy_state == "escalate"
{
row.policy_state = "review".into();
row.investigation_worthy = true;
row.rationale = "corroborator never escalates alone in the scaffold policy".into();
} else if row.feature_name == "S104" && row.policy_state != "watch" {
row.policy_state = "watch".into();
row.investigation_worthy = false;
row.rationale = "sentinel remains watch-only by scaffold design".into();
}
}
rows
}
fn base_policy_state(
spec: &FeatureScaffoldSpec,
grammar_state: ScaffoldGrammarState,
motif_label: DsfbMotifClass,
semantic: Option<&SemanticMatchRecord>,
corroborated: bool,
) -> (&'static str, String) {
match spec.feature_name {
"S059" => {
if semantic
.as_ref()
.is_some_and(|row| row.heuristic_name == RECURRENT_BOUNDARY_APPROACH)
&& grammar_state == ScaffoldGrammarState::BoundaryGrazing
{
("review", "S059 recurrent boundary approach promoted to Review under boundary grazing".into())
} else if semantic
.as_ref()
.is_some_and(|row| row.heuristic_name == PRE_FAILURE_SLOW_DRIFT)
&& matches!(
grammar_state,
ScaffoldGrammarState::SustainedOutwardDrift
| ScaffoldGrammarState::PersistentViolation
)
{
(
if corroborated { "review" } else { "watch" },
"S059 slow drift retained unless grammar and corroboration support promotion".into(),
)
} else {
("silent", "S059 remained below scaffold promotion conditions".into())
}
}
"S133" => {
if semantic
.as_ref()
.is_some_and(|row| row.heuristic_name == PRE_FAILURE_SLOW_DRIFT)
&& grammar_state == ScaffoldGrammarState::SustainedOutwardDrift
{
("review", "S133 remains Review-only when failure-localized slow drift is present".into())
} else {
("silent", "S133 remained below scaffold promotion conditions".into())
}
}
"S123" => {
if semantic
.as_ref()
.is_some_and(|row| row.heuristic_name == TRANSITION_EXCURSION)
&& matches!(
grammar_state,
ScaffoldGrammarState::TransientViolation
| ScaffoldGrammarState::PersistentViolation
)
{
(
if grammar_state == ScaffoldGrammarState::PersistentViolation {
"escalate"
} else {
"review"
},
"S123 transition instability promoted rapidly after grammar-qualified transition excursion".into(),
)
} else if motif_label == DsfbMotifClass::PersistentInstabilityCluster {
("review", "S123 persistent instability cluster retained at Review until corroborated".into())
} else {
("silent", "S123 remained below scaffold promotion conditions".into())
}
}
"S540" | "S128" => {
if semantic
.as_ref()
.is_some_and(|row| row.heuristic_name == TRANSITION_CLUSTER_SUPPORT)
{
(
if corroborated { "review" } else { "watch" },
"secondary corroborator contributes support but cannot escalate alone".into(),
)
} else {
("silent", "secondary corroborator remained structurally isolated".into())
}
}
"S104" => {
if matches!(motif_label, DsfbMotifClass::WatchOnlyBoundaryGrazing)
&& grammar_state == ScaffoldGrammarState::BoundaryGrazing
{
("watch", "sentinel boundary grazing is watch-only by scaffold design".into())
} else {
("silent", "sentinel remained admissible or unsupported".into())
}
}
"S134" | "S275" => (
"silent",
"recall-rescue feature remains outside primary scaffold promotion and is reserved for bounded rescue logic".into(),
),
_ => ("silent", format!("{} is outside the scaffolded policy set", spec.feature_name)),
}
}
fn related_feature_support(
base_states: &BTreeMap<(String, usize), String>,
feature_name: &str,
run_index: usize,
corroborated: bool,
) -> bool {
if corroborated {
return true;
}
match feature_name {
"S059" => {
has_active_state(base_states, "S133", run_index)
|| has_active_state(base_states, "S123", run_index)
}
"S133" => {
has_active_state(base_states, "S059", run_index)
|| has_active_state(base_states, "S123", run_index)
}
"S123" => {
has_active_state(base_states, "S059", run_index)
|| has_active_state(base_states, "S540", run_index)
|| has_active_state(base_states, "S128", run_index)
}
_ => false,
}
}
fn has_active_state(
base_states: &BTreeMap<(String, usize), String>,
feature_name: &str,
run_index: usize,
) -> bool {
base_states
.get(&(feature_name.to_string(), run_index))
.is_some_and(|state| matches!(state.as_str(), "review" | "escalate"))
}
fn feature_scaffold_spec(feature_name: &str) -> Option<&'static FeatureScaffoldSpec> {
FEATURE_SCAFFOLD
.iter()
.find(|spec| spec.feature_name == feature_name)
}
fn group_scaffold_spec(group_name: &str) -> Option<&'static GroupScaffoldSpec> {
GROUP_SCAFFOLD
.iter()
.find(|spec| spec.group_name == group_name)
}
fn rescue_priority_rank(priority: &str) -> usize {
match priority {
"high" => 3,
"medium" => 2,
"low" => 1,
_ => 0,
}
}
fn classify_grammar_label(
grammar_trace: &FeatureGrammarTrace,
run_index: usize,
) -> ScaffoldGrammarState {
let confirmed_state = grammar_trace.states[run_index];
let confirmed_reason = grammar_trace.reasons[run_index];
if confirmed_state == GrammarState::Admissible {
if run_index > 0 && grammar_trace.states[run_index - 1] != GrammarState::Admissible {
ScaffoldGrammarState::Recovery
} else {
ScaffoldGrammarState::Admissible
}
} else if grammar_trace.persistent_violation[run_index] {
ScaffoldGrammarState::PersistentViolation
} else if confirmed_state == GrammarState::Violation
|| confirmed_reason == GrammarReason::EnvelopeViolation
{
ScaffoldGrammarState::TransientViolation
} else if confirmed_reason == GrammarReason::SustainedOutwardDrift {
ScaffoldGrammarState::SustainedOutwardDrift
} else if confirmed_reason == GrammarReason::RecurrentBoundaryGrazing {
ScaffoldGrammarState::BoundaryGrazing
} else if confirmed_reason == GrammarReason::AbruptSlewViolation {
ScaffoldGrammarState::TransientViolation
} else {
ScaffoldGrammarState::BoundaryGrazing
}
}
#[derive(Debug, Clone, Copy)]
struct ExpandedHeuristicMetadata {
allowed_grammar_states: &'static str,
role_class: &'static str,
feature_scope: &'static str,
ambiguity_note: &'static str,
rescue_eligibility_guidance: &'static str,
burden_contribution_class: &'static str,
}
fn expanded_heuristic_entry_metadata(heuristic_name: &str) -> ExpandedHeuristicMetadata {
match heuristic_name {
PRE_FAILURE_SLOW_DRIFT => ExpandedHeuristicMetadata {
allowed_grammar_states: "sustained_outward_drift,persistent_violation",
role_class: "primary_precursor",
feature_scope: "S059 plus S133 only when failure-localized slow drift support is present",
ambiguity_note: "Slow drift remains interpretive and does not identify a unique mechanism.",
rescue_eligibility_guidance: "Rescue-eligible on primary precursor features only.",
burden_contribution_class: "review_burden_candidate",
},
RECURRENT_BOUNDARY_APPROACH => ExpandedHeuristicMetadata {
allowed_grammar_states: "boundary_grazing,sustained_outward_drift",
role_class: "precursor_or_support",
feature_scope: "S059 and compatible recurrent-boundary precursor features",
ambiguity_note: "Repeated boundary approach is structurally meaningful but not uniquely causal.",
rescue_eligibility_guidance: "Use for bounded Review promotion only when persistence is visible.",
burden_contribution_class: "watch_to_review_pressure",
},
TRANSITION_EXCURSION => ExpandedHeuristicMetadata {
allowed_grammar_states: "transient_violation,persistent_violation",
role_class: "transition_instability",
feature_scope: "S123 and compatible transition-instability features",
ambiguity_note: "Transition excursions indicate abrupt structural change, not root-cause identity.",
rescue_eligibility_guidance: "Rescue-eligible for transition features with persistent grammar support.",
burden_contribution_class: "escalation_candidate",
},
PERSISTENT_INSTABILITY_CLUSTER => ExpandedHeuristicMetadata {
allowed_grammar_states: "sustained_outward_drift,persistent_violation",
role_class: "instability_cluster",
feature_scope: "S123 and grouped instability clusters with sustained pressure",
ambiguity_note: "Persistent clusters support detectability more strongly than identifiability.",
rescue_eligibility_guidance: "Use to justify escalation only with grouped corroboration.",
burden_contribution_class: "review_or_escalate_cluster",
},
TRANSITION_CLUSTER_SUPPORT => ExpandedHeuristicMetadata {
allowed_grammar_states: "boundary_grazing,sustained_outward_drift,transient_violation",
role_class: "secondary_corroborator",
feature_scope: "S540,S128 and other corroborating support features",
ambiguity_note: "Support motifs indicate alignment with a primary precursor, not a standalone alarm.",
rescue_eligibility_guidance: "Never rescue to escalation from support features alone.",
burden_contribution_class: "corroboration_support",
},
WATCH_ONLY_BOUNDARY_GRAZING => ExpandedHeuristicMetadata {
allowed_grammar_states: "boundary_grazing",
role_class: "sentinel",
feature_scope: "S104 and low-amplitude sentinel features",
ambiguity_note: "Boundary grazing is deliberately treated as semantically ambiguous and low-confidence.",
rescue_eligibility_guidance: "Not rescue-eligible; retain as watch-only.",
burden_contribution_class: "watch_only_burden",
},
_ => ExpandedHeuristicMetadata {
allowed_grammar_states: "grammar_filtered",
role_class: "generic",
feature_scope: "all_features",
ambiguity_note: "Structural semantics remain non-unique.",
rescue_eligibility_guidance: "Apply only after grammar filtering.",
burden_contribution_class: "generic",
},
}
}
fn semantic_regime_conditions(heuristic_name: &str) -> &'static str {
match heuristic_name {
PRE_FAILURE_SLOW_DRIFT => {
"persistent signed drift, moderate residual growth, and limited abrupt slew"
}
RECURRENT_BOUNDARY_APPROACH => {
"repeated boundary proximity with outward tendency and bounded fragmentation"
}
TRANSITION_EXCURSION => {
"elevated slew burst aligned with a grammar transition or violation onset"
}
PERSISTENT_INSTABILITY_CLUSTER => {
"repeated outward grammar pressure that is not reducible to isolated spikes"
}
TRANSITION_CLUSTER_SUPPORT => {
"corroborating burst or pressure feature aligned with a grouped primary feature"
}
WATCH_ONLY_BOUNDARY_GRAZING => {
"boundary proximity without sufficient persistence or corroboration for Review"
}
_ => "deterministic structural regime",
}
}
fn semantic_applicability_rules(heuristic_name: &str) -> &'static str {
match heuristic_name {
PRE_FAILURE_SLOW_DRIFT => {
"filter by grammar first, then apply only on slow structural precursor features"
}
RECURRENT_BOUNDARY_APPROACH => {
"filter by grammar first, then apply on recurrent boundary-pressure features"
}
TRANSITION_EXCURSION => "filter by grammar first, then apply on abrupt transition features",
PERSISTENT_INSTABILITY_CLUSTER => {
"filter by grammar first, then require sustained grouped pressure"
}
TRANSITION_CLUSTER_SUPPORT => {
"filter by grammar first, then require corroborator scope compatibility"
}
WATCH_ONLY_BOUNDARY_GRAZING => {
"filter by grammar first, then confine to watch-only sentinel handling"
}
_ => "apply only after grammar filtering",
}
}
fn compute_structural_delta_metrics(
residuals: &ResidualSet,
grammar: &GrammarSet,
semantic_matches: &[SemanticMatchRecord],
failure_window_mask: &[bool],
) -> StructuralDeltaMetrics {
let total_violation_points = grammar
.traces
.iter()
.flat_map(|trace| trace.raw_states.iter().copied().enumerate())
.filter(|(_, state)| *state == GrammarState::Violation)
.count();
let pre_failure_violation_points = grammar
.traces
.iter()
.flat_map(|trace| trace.raw_states.iter().copied().enumerate())
.filter(|(run_index, state)| {
*state == GrammarState::Violation && failure_window_mask[*run_index]
})
.count();
let grammar_violation_precision = (total_violation_points > 0)
.then_some(pre_failure_violation_points as f64 / total_violation_points as f64);
let motif_precision_pre_failure = if semantic_matches.is_empty() {
None
} else {
Some(
semantic_matches
.iter()
.filter(|row| failure_window_mask[row.run_index])
.count() as f64
/ semantic_matches.len() as f64,
)
};
let failure_window_points = failure_window_mask.iter().filter(|flag| **flag).count();
let pass_window_points = failure_window_mask
.len()
.saturating_sub(failure_window_points);
let failure_semantic_hits = semantic_matches
.iter()
.filter(|row| failure_window_mask[row.run_index])
.count();
let pass_semantic_hits = semantic_matches
.iter()
.filter(|row| !failure_window_mask[row.run_index])
.count();
let failure_semantic_density = (failure_window_points > 0).then_some(
failure_semantic_hits as f64
/ (failure_window_points * residuals.traces.len().max(1)) as f64,
);
let pass_semantic_density = (pass_window_points > 0).then_some(
pass_semantic_hits as f64 / (pass_window_points * residuals.traces.len().max(1)) as f64,
);
let structural_separation = failure_semantic_density
.zip(pass_semantic_density)
.map(|(failure, pass)| failure - pass);
let precursor_stability = if semantic_matches.is_empty() {
None
} else {
let mut grouped = BTreeMap::<(&str, usize), Vec<usize>>::new();
for row in semantic_matches {
grouped
.entry((row.heuristic_name.as_str(), row.feature_index))
.or_default()
.push(row.run_index);
}
let mut matched_pre_failure_points = 0usize;
let mut stable_pre_failure_points = 0usize;
for runs in grouped.values_mut() {
runs.sort_unstable();
let mut episode_len = 0usize;
let mut previous: Option<usize> = None;
for &run_index in runs.iter() {
if previous.is_some_and(|previous| run_index == previous + 1) {
episode_len += 1;
} else {
episode_len = 1;
}
if failure_window_mask[run_index] {
matched_pre_failure_points += 1;
if episode_len >= 2 {
stable_pre_failure_points += 1;
}
}
previous = Some(run_index);
}
}
(matched_pre_failure_points > 0)
.then_some(stable_pre_failure_points as f64 / matched_pre_failure_points as f64)
};
StructuralDeltaMetrics {
grammar_violation_precision,
motif_precision_pre_failure,
structural_separation,
precursor_stability,
episode_precision: None,
compression_ratio: None,
}
}
fn failure_window_mask(
run_count: usize,
labels: &[i8],
pre_failure_lookback_runs: usize,
) -> Vec<bool> {
let failure_indices = labels
.iter()
.enumerate()
.filter_map(|(index, label)| (*label == 1).then_some(index))
.collect::<Vec<_>>();
let mut mask = vec![false; run_count];
for failure_index in failure_indices {
let start = failure_index.saturating_sub(pre_failure_lookback_runs);
for slot in &mut mask[start..failure_index] {
*slot = true;
}
}
mask
}
fn mean(values: &[f64]) -> Option<f64> {
(!values.is_empty()).then_some(values.iter().sum::<f64>() / values.len() as f64)
}
fn mean_of_records<T>(records: &[T], project: impl Fn(&T) -> f64) -> f64 {
if records.is_empty() {
0.0
} else {
records.iter().map(project).sum::<f64>() / records.len() as f64
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn semantic_candidates_are_grammar_conditioned() {
let candidates = semantic_candidates_for_run(
"S059",
ScaffoldGrammarState::SustainedOutwardDrift,
GrammarReason::SustainedOutwardDrift,
DsfbMotifClass::PreFailureSlowDrift,
);
assert_eq!(candidates, vec![PRE_FAILURE_SLOW_DRIFT]);
let candidates = semantic_candidates_for_run(
"S104",
ScaffoldGrammarState::Admissible,
GrammarReason::Admissible,
DsfbMotifClass::StableAdmissible,
);
assert!(candidates.is_empty());
}
#[test]
fn scaffold_feature_specs_are_deterministic() {
let s059 = feature_scaffold_spec("S059").unwrap();
assert_eq!(s059.role, "primary recurrent-boundary precursor");
assert_eq!(s059.default_policy_ceiling, HeuristicAlertClass::Review);
assert_eq!(s059.preferred_motifs[0], RECURRENT_BOUNDARY_APPROACH);
let s104 = feature_scaffold_spec("S104").unwrap();
assert_eq!(s104.default_policy_ceiling, HeuristicAlertClass::Watch);
assert_eq!(s104.rescue_priority, "none");
let s134 = feature_scaffold_spec("S134").unwrap();
assert_eq!(s134.role, "recall-rescue feature");
assert_eq!(s134.group_name, "ungrouped");
}
}