#![deny(clippy::cast_lossless)]
#![allow(clippy::arithmetic_side_effects)]
use crate::control_types::OverrideFlags;
#[cfg(feature = "std")]
use crate::llmosafe_detection::DetectionResult;
use crate::llmosafe_detection::{
AdversarialDetector, ConfidenceTracker, CusumDetector, DriftDetector, RepetitionDetector,
};
use crate::llmosafe_integration::EscalationPolicy;
use crate::llmosafe_integration::SafetyDecision;
use crate::llmosafe_kernel::{
DynamicStabilityMonitor, KernelError, KernelOutput, ReasoningLoop, StabilityResult, Synapse,
ValidatedSynapse, FLAG_ADVERSARIAL, FLAG_ANOMALY, FLAG_DECAYING, FLAG_DRIFTING,
FLAG_LOW_CONFIDENCE, FLAG_STUCK, U16_MAX_F32,
};
use crate::llmosafe_memory::WorkingMemory;
use crate::llmosafe_pid::{PidConfig, PidState};
#[cfg(feature = "std")]
use crate::ResourceGuard;
pub const STAGE_SIFT: u8 = 0x01;
pub const STAGE_MEMORY: u8 = 0x02;
pub const STAGE_KERNEL: u8 = 0x04;
pub const STAGE_DETECTION: u8 = 0x08;
pub const STAGE_MONITOR: u8 = 0x10;
#[cfg(feature = "std")]
pub const STAGE_BODY: u8 = 0x20;
pub struct PipelineConfig {
pub policy: EscalationPolicy,
pub pid_config: PidConfig,
pub surprise_threshold: i128,
pub max_repetitions: usize,
pub drift_threshold: f32,
pub min_confidence: f32,
pub decay_threshold: usize,
pub monitor_k: u8,
#[cfg(feature = "std")]
pub use_detection_gate: bool,
}
impl Default for PipelineConfig {
fn default() -> Self {
Self {
policy: EscalationPolicy::default(),
pid_config: PidConfig::default(),
surprise_threshold: 58000,
max_repetitions: 3,
drift_threshold: 0.5,
min_confidence: 0.3,
decay_threshold: 3,
monitor_k: 3,
#[cfg(feature = "std")]
use_detection_gate: false,
}
}
}
impl PipelineConfig {
pub fn validate(&self) -> Result<(), &'static str> {
if self.drift_threshold.is_nan() || self.drift_threshold < 0.0 || self.drift_threshold > 1.0
{
return Err("drift_threshold must be in [0.0, 1.0]");
}
if self.min_confidence.is_nan() || self.min_confidence < 0.0 || self.min_confidence > 1.0 {
return Err("min_confidence must be in [0.0, 1.0]");
}
if self.monitor_k < 1 || self.monitor_k > 5 {
return Err("monitor_k must be in [1, 5]");
}
if self.max_repetitions == 0 {
return Err("max_repetitions must be > 0");
}
if self.decay_threshold == 0 {
return Err("decay_threshold must be > 0");
}
self.pid_config.validate()?;
#[cfg(feature = "std")]
{
#[allow(clippy::print_stderr)]
if let Err(warnings) = self.validate_cross_consistency() {
for w in &warnings {
eprintln!("[llmosafe] {}", w);
}
}
}
#[cfg(not(feature = "std"))]
{
let _ = &self.policy; }
Ok(())
}
#[cfg(feature = "std")]
pub fn validate_cross_consistency(&self) -> Result<(), Vec<String>> {
let mut warnings: Vec<String> = Vec::new();
let halt_policy_risk = f32::from(self.policy.halt_entropy) / U16_MAX_F32;
Self::check_cross_pair(
halt_policy_risk,
self.pid_config.halt_gain,
"halt_entropy",
self.policy.halt_entropy,
"halt_gain",
&mut warnings,
);
let escalate_policy_risk = f32::from(self.policy.escalate_entropy) / U16_MAX_F32;
let escalate_pid_equiv = self.pid_config.halt_gain * 0.8_f32;
Self::check_cross_pair(
escalate_policy_risk,
escalate_pid_equiv,
"escalate_entropy",
self.policy.escalate_entropy,
"halt_gain × 0.8",
&mut warnings,
);
let warn_policy_risk = f32::from(self.policy.warn_entropy) / U16_MAX_F32;
Self::check_cross_pair(
warn_policy_risk,
self.pid_config.warn_gain,
"warn_entropy",
self.policy.warn_entropy,
"warn_gain",
&mut warnings,
);
if warnings.is_empty() {
Ok(())
} else {
Err(warnings)
}
}
#[cfg(feature = "std")]
fn check_cross_pair(
policy_risk: f32,
pid_gain: f32,
policy_name: &str,
policy_raw: u16,
pid_name: &str,
warnings: &mut Vec<String>,
) {
let severity = policy_name.split('_').next().unwrap_or(policy_name);
if policy_risk <= f32::EPSILON || pid_gain <= f32::EPSILON {
if (policy_risk - pid_gain).abs() <= CROSS_CONSISTENCY_TOLERANCE {
return;
}
let divergence_pct = (policy_risk - pid_gain).abs() * 100.0_f32;
warnings.push(format!(
"RC-DUAL {} mismatch: policy.{}={} → risk {:.4}, pid_config.{}={:.4} (divergence: {:.1}%, absolute)",
severity, policy_name, policy_raw, policy_risk, pid_name, pid_gain, divergence_pct
));
return;
}
let ratio = policy_risk / pid_gain;
if (1.0_f32 - CROSS_CONSISTENCY_TOLERANCE..=1.0_f32 + CROSS_CONSISTENCY_TOLERANCE)
.contains(&ratio)
{
return;
}
let divergence_pct = (ratio - 1.0_f32).abs() * 100.0_f32;
warnings.push(format!(
"RC-DUAL {} mismatch: policy.{}={} → risk {:.4}, pid_config.{}={:.4} (divergence: {:.1}%)",
severity, policy_name, policy_raw, policy_risk, pid_name, pid_gain, divergence_pct
));
}
}
#[cfg(feature = "std")]
const CROSS_CONSISTENCY_TOLERANCE: f32 = 0.15_f32;
pub struct MemoryStats {
pub mean: f64,
pub variance: f64,
pub trend: f64,
pub is_drifting: bool,
}
pub struct PipelineResult {
pub decision: SafetyDecision,
pub synapse: Synapse,
pub stages_executed: u8,
pub detection_flags: u8,
pub oov_ratio: u8,
pub entropy: u16,
pub surprise: u16,
pub monitor_state: StabilityResult,
#[cfg(feature = "std")]
pub body_pressure: Option<u8>,
pub step_count: usize,
pub kernel_output: Option<KernelOutput>,
pub classifier_score: f32,
}
impl PipelineResult {
pub fn is_safe(&self) -> bool {
matches!(self.decision, SafetyDecision::Proceed)
}
pub fn halt_reason(&self) -> Option<&KernelError> {
match &self.decision {
SafetyDecision::Halt(err, _) | SafetyDecision::Exit(err) => Some(err),
SafetyDecision::Proceed | SafetyDecision::Warn(_) | SafetyDecision::Escalate { .. } => {
None
}
}
}
pub fn kernel_output(&self) -> Option<&KernelOutput> {
self.kernel_output.as_ref()
}
#[cfg(feature = "std")]
pub fn body_pressure(&self) -> u8 {
self.body_pressure.unwrap_or(0)
}
}
pub struct CognitivePipeline<'a, const MEM_SIZE: usize, const MAX_STEPS: usize> {
memory: WorkingMemory<MEM_SIZE>,
reasoning: ReasoningLoop<MAX_STEPS>,
monitor: DynamicStabilityMonitor,
repetition: RepetitionDetector,
drift: DriftDetector,
confidence: ConfidenceTracker,
cusum: CusumDetector,
adversarial: AdversarialDetector,
objective: &'a str,
step_count: usize,
pid_state: PidState,
pid_config: PidConfig,
#[allow(dead_code)]
pub(crate) esc_policy: EscalationPolicy,
#[cfg(feature = "std")]
#[allow(dead_code)]
pub(crate) use_detection_gate: bool,
drift_threshold: f32,
surprise_threshold: i128,
}
impl<'a, const MEM_SIZE: usize, const MAX_STEPS: usize> CognitivePipeline<'a, MEM_SIZE, MAX_STEPS> {
pub fn new(objective: &'a str) -> Self {
let config = PipelineConfig::default();
Self::with_config(objective, config).unwrap_or_else(|_| unreachable!())
}
pub fn with_config(objective: &'a str, config: PipelineConfig) -> Result<Self, &'static str> {
config.validate()?;
Ok(Self {
memory: WorkingMemory::<MEM_SIZE>::new(config.surprise_threshold),
reasoning: ReasoningLoop::<MAX_STEPS>::new(),
monitor: DynamicStabilityMonitor::new(config.monitor_k),
repetition: RepetitionDetector::new(config.max_repetitions),
drift: DriftDetector::new(objective, config.drift_threshold),
confidence: ConfidenceTracker::new(config.min_confidence, config.decay_threshold),
cusum: CusumDetector::new(0.0, 50.0, 200.0),
adversarial: AdversarialDetector::new(),
objective,
step_count: 0,
pid_state: PidState::new(),
pid_config: config.pid_config,
esc_policy: config.policy,
#[cfg(feature = "std")]
use_detection_gate: config.use_detection_gate,
drift_threshold: config.drift_threshold,
surprise_threshold: config.surprise_threshold,
})
}
pub fn process(&mut self, observation: &str) -> PipelineResult {
self.process_ctrl(observation, 0.0, 0)
}
#[cfg(feature = "std")]
pub fn process_with_pressure(
&mut self,
observation: &str,
body_entropy: u16,
pressure: u8,
) -> PipelineResult {
use crate::llmosafe_integration::PressureLevel;
let pressure_level = PressureLevel::from_percentage(pressure);
if pressure_level.requires_action() {
let decision =
self.esc_policy
.decide_with_pressure(body_entropy, 0, false, pressure_level);
let mut synapse = Synapse::new();
synapse.set_raw_entropy(body_entropy);
return PipelineResult {
decision,
synapse,
stages_executed: STAGE_BODY,
detection_flags: 0,
oov_ratio: 0,
entropy: body_entropy,
surprise: 0,
monitor_state: crate::llmosafe_kernel::StabilityResult::Stable,
#[cfg(feature = "std")]
body_pressure: Some(pressure),
step_count: self.step_count,
kernel_output: None,
classifier_score: 0.0,
};
}
let e_body = (f32::from(body_entropy) / 1000.0_f32).clamp(0.0, 1.0);
let mut result = self.process_ctrl(observation, e_body, pressure);
result.stages_executed |= STAGE_BODY;
result.body_pressure = Some(pressure);
result
}
#[cfg(feature = "std")]
pub fn process_safe(
&mut self,
text: &str,
guard: &ResourceGuard,
) -> Result<PipelineResult, KernelError> {
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5);
match guard.check_with_deadline(deadline) {
Ok(_synapse) => Ok(self.process(text)),
Err(KernelError::DeadlineExceeded) => {
Ok(self.process_with_pressure(text, guard.raw_entropy(), guard.pressure()))
}
Err(e) => Err(e),
}
}
pub fn reset_detectors(&mut self) {
self.repetition.reset();
self.confidence.reset();
self.cusum.reset();
self.adversarial = AdversarialDetector::new();
self.monitor.reset();
self.drift = DriftDetector::new(self.objective, self.drift_threshold);
}
pub fn reset_full(&mut self) {
self.memory = WorkingMemory::<MEM_SIZE>::new(self.surprise_threshold);
self.reasoning = ReasoningLoop::<MAX_STEPS>::new();
self.monitor.reset();
self.repetition.reset();
self.drift = DriftDetector::new(self.objective, self.drift_threshold);
self.confidence.reset();
self.cusum.reset();
self.adversarial = AdversarialDetector::new();
self.step_count = 0;
self.pid_state.reset();
}
pub fn pid_state(&self) -> &PidState {
&self.pid_state
}
pub fn memory_stats(&self) -> MemoryStats {
let mean = self.memory.mean_entropy();
let variance = self.memory.entropy_variance();
let trend = self.memory.trend();
MemoryStats {
mean,
variance,
trend,
is_drifting: self.memory.is_drifting(10.0),
}
}
pub fn process_ctrl(&mut self, observation: &str, e_body: f32, pressure: u8) -> PipelineResult {
let _pressure = pressure; let mut stages = 0u8;
stages |= STAGE_SIFT;
let (sifted, sifted_proof, classifier_score) =
crate::llmosafe_sifter::sift_text_with_score(observation);
let entropy = sifted.raw_entropy();
let surprise_val = sifted.raw_surprise();
let oov_ratio = sifted.oov_ratio();
let has_bias = sifted.has_bias();
stages |= STAGE_MEMORY;
let mem_result = self.memory.update(sifted, sifted_proof);
let validated = match mem_result {
Ok((v, _p)) => v,
Err(err) => {
return self.ctrl_result_from_error(err, stages, oov_ratio, entropy, surprise_val);
}
};
stages |= STAGE_KERNEL;
let kernel_synapse = validated.into_inner();
let kernel_entropy = kernel_synapse.raw_entropy();
let kernel_validated = ValidatedSynapse::new(kernel_synapse);
let kernel_result = self
.reasoning
.next_step(kernel_validated, crate::llmosafe_kernel::ValidatedProof(()));
let kernel_synapse_out = match kernel_result {
Ok(()) => {
self.step_count += 1;
validated.into_inner()
}
Err(err) => {
return self.ctrl_result_from_kernel_error(
err,
stages,
oov_ratio,
entropy,
surprise_val,
);
}
};
stages |= STAGE_DETECTION;
self.repetition.observe(observation);
self.drift.observe(observation);
let classifier_prob = f32::from(entropy) / U16_MAX_F32;
self.confidence.observe(classifier_prob);
let _cusum_anomaly = self.cusum.update(f64::from(entropy));
let is_stuck = self.repetition.is_stuck();
let is_drifting = self.drift.is_drifting();
let is_low_confidence = self.confidence.is_low();
let is_decaying = self.confidence.is_decaying();
let anomaly_detected = self.cusum.detected();
let adversarial_detected = self.adversarial.is_adversarial(observation);
let mut flags: u8 = 0;
if is_stuck {
flags |= FLAG_STUCK;
}
if is_drifting {
flags |= FLAG_DRIFTING;
}
if is_low_confidence {
flags |= FLAG_LOW_CONFIDENCE;
}
if is_decaying {
flags |= FLAG_DECAYING;
}
if anomaly_detected {
flags |= FLAG_ANOMALY;
}
if adversarial_detected {
flags |= FLAG_ADVERSARIAL;
}
#[cfg(feature = "std")]
if self.use_detection_gate {
let detection_result = DetectionResult {
is_stuck,
is_drifting,
is_low_confidence,
is_decaying,
adversarial_patterns: if adversarial_detected {
vec!["adversarial"]
} else {
vec![]
},
risk_score: if anomaly_detected { 0.9 } else { 0.0 },
};
let gate_decision =
self.esc_policy
.decide_from_detection(&detection_result, entropy, surprise_val);
if gate_decision.must_halt() {
stages |= STAGE_MONITOR;
let monitor_state = self.monitor.update(u32::from(entropy));
let kernel_output = Some(KernelOutput {
error_kernel: f32::from(kernel_entropy) / U16_MAX_F32,
is_stable: u32::from(kernel_entropy)
< crate::llmosafe_kernel::STABILITY_THRESHOLD as u32,
depth: self.step_count,
});
return PipelineResult {
decision: gate_decision,
synapse: kernel_synapse_out,
stages_executed: stages,
detection_flags: flags,
oov_ratio,
entropy,
surprise: surprise_val,
monitor_state,
#[cfg(feature = "std")]
body_pressure: Some(pressure),
step_count: self.step_count,
kernel_output,
classifier_score,
};
}
}
let pressure_term = (e_body * 100.0_f32) as u8;
let trend = self.memory.trend();
let mem_mean = self.memory.mean_entropy();
let e_mem = ((f64::from(entropy) - mem_mean).abs() as f32 / U16_MAX_F32).clamp(0.0, 1.0);
let e_kernel = (f32::from(kernel_entropy) / U16_MAX_F32).clamp(0.0, 1.0);
let pid_input = crate::control_types::PidInput::new(
e_body,
f32::from(entropy) / U16_MAX_F32,
e_mem,
e_kernel,
trend,
classifier_prob,
has_bias,
flags,
pressure_term,
);
let pure_risk = crate::llmosafe_pid::compute_pid_score_pure(
&pid_input,
&self.pid_config,
&mut self.pid_state,
);
stages |= STAGE_MONITOR;
let monitor_state = self.monitor.update(u32::from(kernel_entropy));
let mut override_flags = OverrideFlags::empty();
if has_bias {
override_flags = override_flags | OverrideFlags::BIAS;
}
if e_body > 0.9 {
override_flags = override_flags | OverrideFlags::EXHAUSTED;
}
if monitor_state != crate::llmosafe_kernel::StabilityResult::Stable {
override_flags = override_flags | OverrideFlags::KERNEL_UNSTABLE;
}
let limited_risk = crate::llmosafe_pid::apply_safety_overrides(
pure_risk,
override_flags,
&self.pid_config,
);
let decision = crate::llmosafe_pid::pid_risk_to_decision(limited_risk, &self.pid_config);
let kernel_output = Some(KernelOutput {
error_kernel: f32::from(kernel_entropy) / U16_MAX_F32,
is_stable: u32::from(kernel_entropy)
< crate::llmosafe_kernel::STABILITY_THRESHOLD as u32,
depth: self.step_count,
});
PipelineResult {
decision,
synapse: kernel_synapse_out,
stages_executed: stages,
detection_flags: flags,
oov_ratio,
entropy,
surprise: surprise_val,
monitor_state,
#[cfg(feature = "std")]
body_pressure: Some(pressure),
step_count: self.step_count,
kernel_output,
classifier_score,
}
}
fn ctrl_result_from_error(
&self,
err: KernelError,
stages: u8,
oov_ratio: u8,
entropy: u16,
surprise_val: u16,
) -> PipelineResult {
let (decision, synapse) = match err {
KernelError::HallucinationDetected => {
let mut s = Synapse::new();
s.set_raw_entropy(entropy);
(
SafetyDecision::Escalate {
entropy,
reason: crate::llmosafe_integration::EscalationReason::Custom(
"hallucination",
),
cooldown_ms: 5000,
},
s,
)
}
KernelError::CognitiveInstability => {
let mut s = Synapse::new();
s.set_raw_entropy(entropy);
(
SafetyDecision::Halt(KernelError::CognitiveInstability, 30000),
s,
)
}
KernelError::BiasHaloDetected => {
let mut s = Synapse::new();
s.set_raw_entropy(entropy);
(
SafetyDecision::Halt(KernelError::BiasHaloDetected, 30000),
s,
)
}
KernelError::DepthExceeded
| KernelError::ResourceExhaustion
| KernelError::SelfMemoryExceeded
| KernelError::DeadlineExceeded => {
let mut s = Synapse::new();
s.set_raw_entropy(entropy);
(SafetyDecision::Halt(err, 30000), s)
}
};
PipelineResult {
decision,
synapse,
stages_executed: stages,
detection_flags: 0,
oov_ratio,
entropy,
surprise: surprise_val,
monitor_state: StabilityResult::Stable,
#[cfg(feature = "std")]
body_pressure: None,
step_count: self.step_count,
kernel_output: None,
classifier_score: 0.0,
}
}
fn ctrl_result_from_kernel_error(
&self,
err: KernelError,
stages: u8,
oov_ratio: u8,
entropy: u16,
surprise_val: u16,
) -> PipelineResult {
let decision = match err {
KernelError::DepthExceeded => SafetyDecision::Escalate {
entropy,
reason: crate::llmosafe_integration::EscalationReason::Custom(
"reasoning depth exceeded",
),
cooldown_ms: 10000,
},
KernelError::BiasHaloDetected => {
SafetyDecision::Halt(KernelError::BiasHaloDetected, 30000)
}
KernelError::CognitiveInstability => {
SafetyDecision::Halt(KernelError::CognitiveInstability, 30000)
}
KernelError::HallucinationDetected
| KernelError::ResourceExhaustion
| KernelError::SelfMemoryExceeded
| KernelError::DeadlineExceeded => SafetyDecision::Halt(err, 30000),
};
let mut err_synapse = Synapse::new();
err_synapse.set_raw_entropy(entropy);
PipelineResult {
decision,
synapse: err_synapse,
stages_executed: stages,
detection_flags: 0,
oov_ratio,
entropy,
surprise: surprise_val,
monitor_state: StabilityResult::Stable,
#[cfg(feature = "std")]
body_pressure: None,
step_count: self.step_count,
kernel_output: None,
classifier_score: 0.0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::llmosafe_kernel::DETECTION_FLAGS_MASK;
#[test]
fn test_pipelineconfig_default_validates() {
let config = PipelineConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_pipelineconfig_validate_rejects_nan_drift_threshold() {
let config = PipelineConfig {
drift_threshold: f32::NAN,
..PipelineConfig::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_pipelineconfig_validate_rejects_out_of_range_confidence() {
let config = PipelineConfig {
min_confidence: 2.0,
..PipelineConfig::default()
};
assert!(config.validate().is_err());
let config = PipelineConfig {
min_confidence: -0.1,
..PipelineConfig::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_pipelineconfig_validate_rejects_zero_monitor_k() {
let config = PipelineConfig {
monitor_k: 0,
..PipelineConfig::default()
};
assert!(config.validate().is_err());
let config = PipelineConfig {
monitor_k: 6,
..PipelineConfig::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_pipelineconfig_validate_rejects_zero_max_repetitions() {
let config = PipelineConfig {
max_repetitions: 0,
..PipelineConfig::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_pipelineconfig_validate_rejects_zero_decay_threshold() {
let config = PipelineConfig {
decay_threshold: 0,
..PipelineConfig::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_pipelineresult_is_safe() {
let mut synapse = Synapse::new();
synapse.set_raw_entropy(100);
let result = PipelineResult {
decision: SafetyDecision::Proceed,
synapse,
stages_executed: STAGE_SIFT | STAGE_MEMORY,
detection_flags: 0,
oov_ratio: 0,
entropy: 100,
surprise: 0,
monitor_state: StabilityResult::Stable,
#[cfg(feature = "std")]
body_pressure: None,
step_count: 1,
kernel_output: None,
classifier_score: 0.0,
};
assert!(result.is_safe());
assert!(result.halt_reason().is_none());
}
#[test]
fn test_pipelineresult_halt_reason() {
let synapse = Synapse::new();
let result = PipelineResult {
decision: SafetyDecision::Halt(KernelError::CognitiveInstability, 30000),
synapse,
stages_executed: STAGE_SIFT,
detection_flags: 0,
oov_ratio: 0,
entropy: 51000,
surprise: 0,
monitor_state: StabilityResult::Stable,
#[cfg(feature = "std")]
body_pressure: None,
step_count: 0,
kernel_output: None,
classifier_score: 0.0,
};
assert!(!result.is_safe());
assert_eq!(
*result.halt_reason().unwrap(),
KernelError::CognitiveInstability
);
}
#[test]
fn test_cognitive_pipeline_new_creates_with_defaults() {
let pipeline = CognitivePipeline::<64, 10>::new("test objective");
assert_eq!(pipeline.objective, "test objective");
assert_eq!(pipeline.step_count, 0);
}
#[test]
fn test_cognitive_pipeline_with_config_validates() {
let config = PipelineConfig::default();
let pipeline = CognitivePipeline::<64, 10>::with_config("test", config);
assert!(pipeline.is_ok());
}
#[test]
fn test_cognitive_pipeline_with_config_rejects_invalid() {
let config = PipelineConfig {
drift_threshold: f32::NAN,
..PipelineConfig::default()
};
let pipeline = CognitivePipeline::<64, 10>::with_config("test", config);
assert!(pipeline.is_err());
}
#[test]
fn test_process_safe_text_returns_proceed() {
let mut pipeline = CognitivePipeline::<64, 10>::new("test objective");
let result = pipeline.process("a completely ordinary sentence about everyday topics");
let _entropy: u16 = result.entropy; let _surprise: u16 = result.surprise;
assert!(result.decision.severity() <= 4);
}
#[test]
fn test_process_returns_pipeline_result_with_synapse() {
let mut pipeline = CognitivePipeline::<64, 10>::new("test");
let result = pipeline.process("checking some input text here");
assert!(result.stages_executed & STAGE_SIFT != 0);
let _ = result.entropy;
let _ = result.surprise;
assert!(result.detection_flags <= DETECTION_FLAGS_MASK);
}
#[test]
fn test_reset_detectors_preserves_step_count() {
let mut pipeline = CognitivePipeline::<64, 10>::new("test");
let _ = pipeline.process("step one");
let _ = pipeline.process("step two");
let before = pipeline.step_count;
pipeline.reset_detectors();
assert_eq!(pipeline.step_count, before);
}
#[test]
fn test_reset_full_clears_everything() {
let mut pipeline = CognitivePipeline::<64, 10>::new("test");
let _ = pipeline.process("this is a normal observation about weather");
let _ = pipeline.process("another normal sentence for testing");
pipeline.reset_full();
assert_eq!(pipeline.step_count, 0);
let result = pipeline.process("completely normal text after reset");
assert!(result.stages_executed & STAGE_SIFT != 0);
}
#[test]
fn test_detection_flags_bitmask_uniqueness() {
assert_ne!(FLAG_STUCK, 0);
assert_ne!(FLAG_DRIFTING, 0);
assert_ne!(FLAG_LOW_CONFIDENCE, 0);
assert_ne!(FLAG_DECAYING, 0);
assert_ne!(FLAG_ANOMALY, 0);
assert_ne!(FLAG_ADVERSARIAL, 0);
let combined = FLAG_STUCK
| FLAG_DRIFTING
| FLAG_LOW_CONFIDENCE
| FLAG_DECAYING
| FLAG_ANOMALY
| FLAG_ADVERSARIAL;
assert_eq!(combined, DETECTION_FLAGS_MASK);
}
#[test]
fn test_synapse_detection_flags_roundtrip() {
let mut synapse = Synapse::new();
synapse.set_detection_flags(FLAG_STUCK | FLAG_ANOMALY);
assert_eq!(synapse.detection_flags(), FLAG_STUCK | FLAG_ANOMALY);
synapse.set_detection_flags(0);
assert_eq!(synapse.detection_flags(), 0);
synapse.set_detection_flags(DETECTION_FLAGS_MASK);
assert_eq!(synapse.detection_flags(), DETECTION_FLAGS_MASK);
}
#[test]
fn test_synapse_oov_ratio_roundtrip() {
let mut synapse = Synapse::new();
synapse.set_oov_ratio(128);
assert_eq!(synapse.oov_ratio(), 128);
synapse.set_oov_ratio(0);
assert_eq!(synapse.oov_ratio(), 0);
synapse.set_oov_ratio(255);
assert_eq!(synapse.oov_ratio(), 255);
}
#[test]
fn test_synapse_clear_detection() {
let mut synapse = Synapse::new();
synapse.set_detection_flags(FLAG_STUCK | FLAG_DRIFTING);
synapse.set_oov_ratio(200);
synapse.clear_detection();
assert_eq!(synapse.detection_flags(), 0);
assert_eq!(synapse.oov_ratio(), 0);
}
#[test]
fn test_synapse_detection_independent_from_entropy() {
let mut synapse = Synapse::new();
synapse.set_raw_entropy(12345);
synapse.set_detection_flags(FLAG_ANOMALY);
assert_eq!(synapse.raw_entropy(), 12345);
assert_eq!(synapse.detection_flags(), FLAG_ANOMALY);
synapse.set_oov_ratio(100);
assert_eq!(synapse.raw_entropy(), 12345);
assert_eq!(synapse.oov_ratio(), 100);
}
#[test]
fn test_synapse_detection_does_not_affect_validate() {
let mut synapse = Synapse::new();
synapse.set_raw_entropy(500);
synapse.set_has_bias(false);
synapse.set_detection_flags(DETECTION_FLAGS_MASK);
synapse.set_oov_ratio(255);
assert!(synapse.validate().is_ok());
let bits = u128::from_le_bytes(synapse.into_bytes());
let reconstructed = Synapse::from_raw_u128(bits);
assert_eq!(reconstructed.detection_flags(), DETECTION_FLAGS_MASK);
assert_eq!(reconstructed.oov_ratio(), 255);
assert_eq!(reconstructed.raw_entropy(), 500);
}
#[test]
fn test_process_with_same_text_triggers_stuck() {
let mut pipeline = CognitivePipeline::<64, 10>::new("test");
let same = "the outdoor temperature readings indicate mild conditions";
for _ in 0..5 {
let result = pipeline.process(same);
assert!(result.detection_flags <= DETECTION_FLAGS_MASK);
}
}
#[test]
fn test_process_with_drifting_text() {
let mut pipeline =
CognitivePipeline::<64, 10>::new("rust safety library performance analysis");
let result = pipeline.process("pizza recipes with extra cheese toppings");
assert!(result.detection_flags & FLAG_DRIFTING != 0);
}
#[test]
fn test_process_max_steps() {
let mut pipeline = CognitivePipeline::<64, 2>::new("test");
let mut last_step = 0usize;
for _ in 0..5 {
let result = pipeline.process("checking safety of different input text");
last_step = result.step_count;
if result.step_count >= 2 {
break;
}
}
assert!(last_step <= 2);
}
#[cfg(feature = "std")]
#[test]
fn test_process_with_pressure_nominal_proceeds() {
let mut pipeline = CognitivePipeline::<64, 10>::new("test");
let result = pipeline.process_with_pressure(
"how do i write a function to sort a list in python",
100,
10,
);
assert!(result.body_pressure.is_some());
}
#[cfg(feature = "std")]
#[test]
fn test_process_with_pressure_critical_escalates() {
let mut pipeline = CognitivePipeline::<64, 10>::new("test");
let result = pipeline.process_with_pressure(
"how do i write a function to sort a list in python",
500,
60,
);
assert!(result.decision.is_blocking());
assert_eq!(result.body_pressure, Some(60));
}
#[cfg(feature = "std")]
#[test]
fn test_process_with_pressure_emergency_halt() {
let mut pipeline = CognitivePipeline::<64, 10>::new("test");
let result = pipeline.process_with_pressure(
"how do i write a function to sort a list in python",
800,
90,
);
assert!(result.decision.is_blocking());
assert_eq!(result.body_pressure, Some(90));
}
#[test]
fn test_process_ctrl_returns_valid_result() {
let mut pipeline = CognitivePipeline::<64, 10>::new("test objective");
let result = pipeline.process_ctrl("a completely ordinary sentence", 0.0, 0);
assert!(result.stages_executed & STAGE_SIFT != 0);
assert!(result.decision.severity() <= 4);
assert!(
result.entropy > 0,
"entropy must be non-zero for valid input"
);
}
#[test]
fn test_process_ctrl_memory_output_has_bounded_error() {
let mut pipeline = CognitivePipeline::<64, 10>::new("test");
let result = pipeline.process_ctrl("safety test input observation", 0.0, 0);
assert!(result.stages_executed & STAGE_MEMORY != 0);
assert!(result.detection_flags <= DETECTION_FLAGS_MASK);
}
#[cfg(feature = "std")]
#[test]
fn test_detection_gate_enabled_falls_through_to_pid() {
let config = PipelineConfig {
use_detection_gate: true,
..PipelineConfig::default()
};
let mut pipeline =
CognitivePipeline::<64, 10>::with_config("test objective", config).unwrap();
let result = pipeline.process("a completely ordinary sentence about everyday topics");
assert!(
result.stages_executed & STAGE_DETECTION != 0,
"detection stage must execute when detection gate is enabled"
);
assert_eq!(
result.stages_executed & (STAGE_SIFT | STAGE_MEMORY | STAGE_KERNEL | STAGE_DETECTION),
STAGE_SIFT | STAGE_MEMORY | STAGE_KERNEL | STAGE_DETECTION
);
}
#[cfg(feature = "std")]
#[test]
fn test_detection_gate_must_halt_triggers_early_return() {
let config = PipelineConfig {
use_detection_gate: true,
..PipelineConfig::default()
};
let mut pipeline =
CognitivePipeline::<64, 10>::with_config("test objective", config).unwrap();
let result = pipeline.process("text triggering detection analysis in the pipeline");
assert!(
result.stages_executed & STAGE_DETECTION != 0,
"detection stage must be set when detection gate is enabled"
);
assert!(result.decision.severity() <= 4);
}
#[cfg(feature = "std")]
#[test]
fn test_detection_gate_stuck_falls_through_to_pid() {
let config = PipelineConfig {
use_detection_gate: true,
max_repetitions: 3,
..PipelineConfig::default()
};
let mut pipeline =
CognitivePipeline::<64, 20>::with_config("test objective", config).unwrap();
let same = "the outdoor temperature readings indicate mild conditions";
for _ in 0..6 {
let result = pipeline.process(same);
assert!(
result.stages_executed & STAGE_DETECTION != 0,
"detection stage must run on every iteration"
);
assert!(result.decision.severity() <= 4);
}
}
#[test]
fn test_process_ctrl_memory_error_hallucination_detected() {
let config = PipelineConfig {
surprise_threshold: 1, ..PipelineConfig::default()
};
let mut pipeline = CognitivePipeline::<64, 10>::with_config("test", config).unwrap();
let result = pipeline.process_ctrl("testing the memory error pathway in pipeline", 0.0, 0);
assert!(result.stages_executed & STAGE_MEMORY != 0);
assert_eq!(result.stages_executed & STAGE_KERNEL, 0);
assert_eq!(result.stages_executed & STAGE_DETECTION, 0);
assert!(result.decision.is_blocking());
assert!(result.kernel_output.is_none());
}
#[test]
fn test_process_ctrl_kernel_error_depth_exceeded() {
let mut pipeline = CognitivePipeline::<64, 1>::new("test objective");
let safe_text = "a completely ordinary sentence about everyday topics";
let result1 = pipeline.process_ctrl(safe_text, 0.0, 0);
assert!(
result1.stages_executed & STAGE_KERNEL != 0,
"kernel stage must execute on first call; text may be triggering memory error"
);
assert_eq!(pipeline.step_count, 1);
let result2 = pipeline.process_ctrl(safe_text, 0.0, 0);
assert!(result2.stages_executed & STAGE_KERNEL != 0);
assert_eq!(result2.stages_executed & STAGE_DETECTION, 0);
assert_eq!(result2.stages_executed & STAGE_MONITOR, 0);
assert!(result2.decision.is_blocking());
assert!(result2.kernel_output.is_none());
}
#[cfg(feature = "std")]
#[test]
fn test_process_with_pressure_elevated_proceeds() {
let mut pipeline = CognitivePipeline::<64, 10>::new("test objective");
let result = pipeline.process_with_pressure(
"safe text for elevated pressure test",
350, 35, );
assert!(result.stages_executed & STAGE_SIFT != 0);
assert!(result.stages_executed & STAGE_MEMORY != 0);
assert!(result.stages_executed & STAGE_KERNEL != 0);
assert!(result.stages_executed & STAGE_DETECTION != 0);
assert_eq!(result.body_pressure, Some(35));
assert!(result.is_safe() || result.decision.severity() <= 4);
}
#[cfg(feature = "std")]
#[test]
fn test_process_safe_returns_ok_for_safe_guard() {
let mut pipeline = CognitivePipeline::<64, 10>::new("test");
let guard = ResourceGuard::for_testing(1024 * 1024, 100, 10);
let result = pipeline.process_safe("safe input text for guarded pipeline", &guard);
assert!(result.is_ok(), "process_safe should succeed for safe guard");
let pipeline_result = result.unwrap();
assert!(pipeline_result.decision.severity() <= 4);
}
#[cfg(feature = "std")]
#[test]
fn test_check_with_deadline_zero_deadline_returns_exceeded() {
let guard = ResourceGuard::for_testing(1024 * 1024, 100, 60);
let past = std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(1))
.unwrap_or(std::time::Instant::now());
let result = guard.check_with_deadline(past);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), KernelError::DeadlineExceeded);
}
#[test]
fn test_pipelineconfig_validate_rejects_invalid_pid_config() {
let pid_config = PidConfig {
integrator_decay: -0.1,
..PidConfig::default()
};
let config = PipelineConfig {
pid_config,
..PipelineConfig::default()
};
assert!(
config.validate().is_err(),
"negative integrator_decay must be rejected by validate"
);
}
#[test]
fn test_pipelineconfig_validate_rejects_warn_gain_ge_halt_gain() {
let pid_config = PidConfig {
warn_gain: 0.9,
halt_gain: 0.9, ..PidConfig::default()
};
let config = PipelineConfig {
pid_config,
..PipelineConfig::default()
};
assert!(
config.validate().is_err(),
"warn_gain >= halt_gain must be rejected by validate"
);
}
#[cfg(feature = "std")]
#[test]
fn test_pipelineconfig_cross_consistency_defaults() {
let config = PipelineConfig::default();
assert!(config.validate().is_ok());
let result = config.validate_cross_consistency();
assert!(
result.is_err(),
"default configs have known divergence in halt and escalate thresholds"
);
let warnings = result.unwrap_err();
assert_eq!(
warnings.len(),
2,
"expected 2 warnings for halt_entropy and escalate_entropy divergence, got: {:?}",
warnings
);
assert!(
warnings[0].contains("halt"),
"first warning should be about halt_entropy: {}",
warnings[0]
);
assert!(
warnings[1].contains("escalate"),
"second warning should be about escalate_entropy: {}",
warnings[1]
);
}
#[cfg(feature = "std")]
#[test]
fn test_pipelineconfig_cross_consistency_aligned() {
let config = PipelineConfig {
policy: EscalationPolicy {
halt_entropy: 65535, escalate_entropy: 52428, warn_entropy: 32768, ..EscalationPolicy::default()
},
pid_config: PidConfig {
halt_gain: 1.0,
warn_gain: 0.5,
..PidConfig::default()
},
..PipelineConfig::default()
};
assert!(config.validate().is_ok());
assert!(
config.validate_cross_consistency().is_ok(),
"aligned thresholds should pass cross-consistency"
);
}
#[cfg(feature = "std")]
#[test]
fn test_pipelineconfig_cross_consistency_misaligned() {
let config = PipelineConfig {
policy: EscalationPolicy {
halt_entropy: 10000,
escalate_entropy: 1,
warn_entropy: 1,
..EscalationPolicy::default()
},
pid_config: PidConfig {
halt_gain: 1.0,
warn_gain: 0.99,
..PidConfig::default()
},
..PipelineConfig::default()
};
assert!(config.validate().is_ok());
let result = config.validate_cross_consistency();
assert!(
result.is_err(),
"intentionally misaligned configs must produce warnings"
);
let warnings = result.unwrap_err();
assert!(
warnings.len() >= 2,
"expected at least 2 warnings for misaligned thresholds, got: {:?}",
warnings
);
for w in &warnings {
assert!(
w.contains("halt") || w.contains("escalate") || w.contains("warn"),
"warning must identify severity: {}",
w
);
}
}
}