Fix violation prompt window acknowledgement

This commit is contained in:
2026-05-26 10:11:49 -04:00
parent 3ff868c4e8
commit 3b6ba569ce
+82 -3
View File
@@ -120,6 +120,7 @@ struct App {
transition_return_target: String,
transition_duration_str: String,
violation_message: String,
violation_window_snapshot: Option<window::WindowSnapshot>,
violation_dismissal_reason: String,
current_window_title: Rc<String>,
evidence_health: EvidenceHealth,
@@ -209,6 +210,7 @@ impl App {
transition_return_target: String::new(),
transition_duration_str: "2".to_string(),
violation_message: String::new(),
violation_window_snapshot: None,
violation_dismissal_reason: String::new(),
current_window_title: window_snapshot.title.into(),
evidence_health: window_snapshot.health,
@@ -314,6 +316,7 @@ impl App {
fn enter_violation_prompt(&mut self, window_snapshot: &window::WindowSnapshot) {
self.violation_message =
format!("{}: {}", constants::VIOLATION_STATUS, window_snapshot.title);
self.violation_window_snapshot = Some(window_snapshot.clone());
self.violation_dismissal_reason.clear();
self.state = State::ViolationPrompt;
}
@@ -323,15 +326,28 @@ impl App {
return Ok(());
}
let violation_window_snapshot = self.violation_window_snapshot.clone();
let window_title = violation_window_snapshot
.as_ref()
.map(|snapshot| snapshot.title.clone())
.unwrap_or_else(|| self.current_window_title.as_ref().clone());
let evidence_health = violation_window_snapshot
.as_ref()
.map(|snapshot| snapshot.health.clone())
.unwrap_or_else(|| self.evidence_health.clone());
self.session_controller.record_violation(
self.violation_dismissal_reason.clone(),
serde_json::json!({
"message": self.violation_message,
"window_title": self.current_window_title.as_ref(),
"evidence_health": self.evidence_health,
"window_title": window_title,
"evidence_health": evidence_health,
}),
)?;
self.acknowledge_unknown_window(&window::get_snapshot());
if let Some(window_snapshot) = violation_window_snapshot.as_ref() {
self.acknowledge_unknown_window(window_snapshot);
}
self.violation_window_snapshot = None;
self.state = State::InProgress;
Ok(())
}
@@ -1372,6 +1388,69 @@ mod tests {
let _ = fs::remove_file(path);
}
#[test]
fn violation_dismissal_acknowledges_prompted_window_not_current_foreground() {
let path = unique_event_log_path("violation-prompt-original-ack");
let mut app = App::new_with_event_log_path(&path).unwrap();
let prompted_unknown = window::WindowSnapshot {
title: format!(
"Prompted Unknown Window {}",
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos()
),
class: Some("prompted-unknown-class".to_string()),
health: antidrift::domain::EvidenceHealth::Available,
};
let dismissal_foreground = window::WindowSnapshot {
title: "AntiDrift Terminal".to_string(),
class: Some("terminal".to_string()),
health: antidrift::domain::EvidenceHealth::Degraded(
"terminal foreground during dismissal".to_string(),
),
};
app.user_intention = "write tests".to_string();
app.user_success_condition = "tests pass".to_string();
app.user_duration_str = "1".to_string();
app.to_in_progress().unwrap();
app.enter_violation_prompt(&prompted_unknown);
app.current_window_title = Rc::new(dismissal_foreground.title.clone());
app.evidence_health = dismissal_foreground.health.clone();
app.violation_dismissal_reason = "Needed to inspect docs".to_string();
app.dismiss_violation_if_valid().unwrap();
assert_eq!(app.state, State::InProgress);
assert!(app.is_unknown_window_acknowledged(&prompted_unknown));
assert!(!app.is_unknown_window_acknowledged(&dismissal_foreground));
let records = EventLog::open(&path).unwrap().records().unwrap();
let violation = records
.iter()
.find(|record| record.event_type == EventType::ViolationObserved)
.expect("dismissal should record a violation");
assert_eq!(
violation.payload_json["evidence"]["window_title"],
prompted_unknown.title
);
assert_eq!(
violation.payload_json["evidence"]["message"],
format!(
"{}: {}",
constants::VIOLATION_STATUS,
prompted_unknown.title
)
);
assert_eq!(
violation.payload_json["evidence"]["evidence_health"],
serde_json::json!("available")
);
let _ = fs::remove_file(path);
}
#[test]
fn rating_completion_returns_controller_to_planning_for_next_session() {
let path = unique_event_log_path("rating-completion");