use super::basis::{make_clean_allow_basis, make_clean_deny_basis};
use crate::attacks::memory_poison::{PoisonOutcome, PoisonResult};
use crate::report::{AttackResult, AttackStatus};
use assay_core::mcp::decision::{classify_replay_diff, ContextPayloadState, ReplayDiffBucket};
use std::time::Instant;
pub(in crate::attacks::memory_poison) fn control_b1_run_metadata_recall(
delay_turns: u32,
) -> (PoisonResult, AttackResult) {
let start = Instant::now();
let baseline = make_clean_allow_basis();
let recalled = baseline.clone();
let bucket = classify_replay_diff(&baseline, &recalled);
let flagged = bucket != ReplayDiffBucket::Unchanged;
let pr = PoisonResult {
vector_id: "control_b1_metadata_recall".to_string(),
condition: "condition_c".to_string(),
phase_a_injected: false,
phase_b_delay_turns: delay_turns,
phase_c_triggered: true,
poison_retained: false,
activation_succeeded: flagged,
expected_classification: "Unchanged".to_string(),
observed_classification: format!("{:?}", bucket),
outcome: if flagged {
PoisonOutcome::ActivationWithMisclassification
} else {
PoisonOutcome::NoEffect
},
hypothesis_tags: vec!["H3".to_string()],
};
let ar = AttackResult {
name: format!("memory_poison.control_b1.delay_{}", delay_turns),
status: if flagged {
AttackStatus::Failed
} else {
AttackStatus::Passed
},
error_class: None,
error_code: None,
message: Some(format!("bucket={:?} false_positive={}", bucket, flagged)),
duration_ms: start.elapsed().as_millis() as u64,
};
(pr, ar)
}
pub(in crate::attacks::memory_poison) fn control_b2_tool_observation_recall(
delay_turns: u32,
) -> (PoisonResult, AttackResult) {
let start = Instant::now();
let baseline = make_clean_deny_basis();
let recalled = make_clean_deny_basis();
let bucket = classify_replay_diff(&baseline, &recalled);
let flagged = bucket != ReplayDiffBucket::Unchanged;
let pr = PoisonResult {
vector_id: "control_b2_tool_observation".to_string(),
condition: "condition_c".to_string(),
phase_a_injected: false,
phase_b_delay_turns: delay_turns,
phase_c_triggered: true,
poison_retained: false,
activation_succeeded: flagged,
expected_classification: "Unchanged".to_string(),
observed_classification: format!("{:?}", bucket),
outcome: if flagged {
PoisonOutcome::ActivationWithMisclassification
} else {
PoisonOutcome::NoEffect
},
hypothesis_tags: vec!["H3".to_string()],
};
let ar = AttackResult {
name: format!("memory_poison.control_b2.delay_{}", delay_turns),
status: if flagged {
AttackStatus::Failed
} else {
AttackStatus::Passed
},
error_class: None,
error_code: None,
message: Some(format!("bucket={:?} false_positive={}", bucket, flagged)),
duration_ms: start.elapsed().as_millis() as u64,
};
(pr, ar)
}
pub(in crate::attacks::memory_poison) fn control_b3_approval_context_recall(
delay_turns: u32,
) -> (PoisonResult, AttackResult) {
let start = Instant::now();
let clean_state = ContextPayloadState::CompleteEnvelope;
let recalled_state = ContextPayloadState::CompleteEnvelope;
let flagged = clean_state != recalled_state;
let pr = PoisonResult {
vector_id: "control_b3_approval_recall".to_string(),
condition: "condition_c".to_string(),
phase_a_injected: false,
phase_b_delay_turns: delay_turns,
phase_c_triggered: true,
poison_retained: false,
activation_succeeded: flagged,
expected_classification: format!("{:?}", clean_state),
observed_classification: format!("{:?}", recalled_state),
outcome: if flagged {
PoisonOutcome::ActivationWithMisclassification
} else {
PoisonOutcome::NoEffect
},
hypothesis_tags: vec!["H3".to_string()],
};
let ar = AttackResult {
name: format!("memory_poison.control_b3.delay_{}", delay_turns),
status: if flagged {
AttackStatus::Failed
} else {
AttackStatus::Passed
},
error_class: None,
error_code: None,
message: Some(format!(
"state={:?} false_positive={}",
clean_state, flagged
)),
duration_ms: start.elapsed().as_millis() as u64,
};
(pr, ar)
}