use super::config::ThresholdConfig;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AdjustmentReason {
EnergySpike {
magnitude: f32,
},
SustainedIncoherence {
duration_secs: f32,
},
SuccessPattern {
similarity: f32,
},
ManualOverride,
BackgroundLearning {
samples: usize,
},
ColdStart,
RegimeChange {
regime_id: String,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThresholdAdjustment {
pub new_thresholds: ThresholdConfig,
pub reason: AdjustmentReason,
pub confidence: f32,
pub urgent: bool,
pub delta: ThresholdDelta,
pub timestamp_ms: u64,
}
impl ThresholdAdjustment {
pub fn new(
current: &ThresholdConfig,
new_thresholds: ThresholdConfig,
reason: AdjustmentReason,
confidence: f32,
) -> Self {
let delta = ThresholdDelta {
reflex_delta: new_thresholds.reflex - current.reflex,
retrieval_delta: new_thresholds.retrieval - current.retrieval,
heavy_delta: new_thresholds.heavy - current.heavy,
};
let urgent = matches!(
reason,
AdjustmentReason::EnergySpike { magnitude } if magnitude > 0.5
);
Self {
new_thresholds,
reason,
confidence,
urgent,
delta,
timestamp_ms: current_time_ms(),
}
}
pub fn for_energy_spike(
current: &ThresholdConfig,
spike_magnitude: f32,
) -> Self {
let factor = 1.0 - (spike_magnitude * 0.5).min(0.4);
let new = ThresholdConfig {
reflex: current.reflex * factor,
retrieval: current.retrieval * factor,
heavy: current.heavy * factor,
persistence_window_secs: current.persistence_window_secs,
};
Self::new(
current,
new,
AdjustmentReason::EnergySpike { magnitude: spike_magnitude },
0.8 + spike_magnitude * 0.1,
)
}
pub fn from_success_pattern(
current: &ThresholdConfig,
pattern_thresholds: ThresholdConfig,
similarity: f32,
) -> Self {
let new = current.lerp(&pattern_thresholds, similarity * 0.5);
Self::new(
current,
new,
AdjustmentReason::SuccessPattern { similarity },
similarity,
)
}
pub fn is_significant(&self) -> bool {
self.delta.max_abs_delta() > 0.01 && self.confidence > 0.5
}
pub fn magnitude(&self) -> f32 {
self.delta.max_abs_delta()
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct ThresholdDelta {
pub reflex_delta: f32,
pub retrieval_delta: f32,
pub heavy_delta: f32,
}
impl ThresholdDelta {
pub fn max_abs_delta(&self) -> f32 {
self.reflex_delta
.abs()
.max(self.retrieval_delta.abs())
.max(self.heavy_delta.abs())
}
pub fn total_magnitude(&self) -> f32 {
(self.reflex_delta.powi(2)
+ self.retrieval_delta.powi(2)
+ self.heavy_delta.powi(2))
.sqrt()
}
}
fn current_time_ms() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_energy_spike_adjustment() {
let current = ThresholdConfig::default();
let adj = ThresholdAdjustment::for_energy_spike(¤t, 0.5);
assert!(adj.new_thresholds.reflex < current.reflex);
assert!(adj.confidence > 0.8);
assert!(adj.urgent);
}
#[test]
fn test_success_pattern_adjustment() {
let current = ThresholdConfig::default();
let pattern = ThresholdConfig::conservative();
let adj = ThresholdAdjustment::from_success_pattern(¤t, pattern, 0.9);
assert!(adj.new_thresholds.reflex < current.reflex);
assert!(adj.confidence > 0.8);
}
#[test]
fn test_threshold_delta() {
let delta = ThresholdDelta {
reflex_delta: 0.1,
retrieval_delta: -0.2,
heavy_delta: 0.05,
};
assert!((delta.max_abs_delta() - 0.2).abs() < 0.001);
}
}