Fix post-dismissal prompt handoff
This commit is contained in:
+106
-3
@@ -133,6 +133,7 @@ struct App {
|
||||
session_ratings_index: usize,
|
||||
session_results: Vec<SessionResult>,
|
||||
acknowledged_unknown_window: Option<WindowIdentity>,
|
||||
acknowledged_unknown_window_handoff_pending: bool,
|
||||
last_tick_50ms: Instant,
|
||||
last_tick_1s: Instant,
|
||||
}
|
||||
@@ -223,6 +224,7 @@ impl App {
|
||||
session_ratings_index: 0,
|
||||
session_results: Vec::new(),
|
||||
acknowledged_unknown_window: None,
|
||||
acknowledged_unknown_window_handoff_pending: false,
|
||||
last_tick_50ms: now,
|
||||
last_tick_1s: now,
|
||||
})
|
||||
@@ -460,9 +462,16 @@ impl App {
|
||||
}
|
||||
|
||||
fn tick_active_session(&mut self) -> Result<()> {
|
||||
self.tick_active_session_with_snapshot(window::get_snapshot())
|
||||
}
|
||||
|
||||
fn tick_active_session_with_snapshot(
|
||||
&mut self,
|
||||
window_snapshot: window::WindowSnapshot,
|
||||
) -> Result<()> {
|
||||
let elapsed = self.session_start.elapsed();
|
||||
self.session_remaining = self.user_duration.saturating_sub(elapsed);
|
||||
let window_snapshot = update_session_stats(self);
|
||||
let window_snapshot = update_session_stats_with_snapshot(self, window_snapshot);
|
||||
|
||||
let should_prompt = self.state == State::InProgress
|
||||
&& self.session_controller.runtime_state() == RuntimeState::Active
|
||||
@@ -495,6 +504,7 @@ impl App {
|
||||
|
||||
fn acknowledge_unknown_window(&mut self, window_snapshot: &window::WindowSnapshot) {
|
||||
self.acknowledged_unknown_window = Some(WindowIdentity::from(window_snapshot));
|
||||
self.acknowledged_unknown_window_handoff_pending = true;
|
||||
}
|
||||
|
||||
fn is_unknown_window_acknowledged(&mut self, window_snapshot: &window::WindowSnapshot) -> bool {
|
||||
@@ -503,7 +513,14 @@ impl App {
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.acknowledged_unknown_window_handoff_pending
|
||||
&& is_post_dismissal_app_foreground(window_snapshot)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
self.acknowledged_unknown_window = None;
|
||||
self.acknowledged_unknown_window_handoff_pending = false;
|
||||
false
|
||||
}
|
||||
|
||||
@@ -628,6 +645,13 @@ fn should_prompt_for_unknown_window(
|
||||
!(title_matches || class_matches)
|
||||
}
|
||||
|
||||
fn is_post_dismissal_app_foreground(window_snapshot: &window::WindowSnapshot) -> bool {
|
||||
window_snapshot
|
||||
.title
|
||||
.to_lowercase()
|
||||
.contains(&constants::APP_TITLE.to_lowercase())
|
||||
}
|
||||
|
||||
fn usable_title_evidence(window_snapshot: &window::WindowSnapshot) -> bool {
|
||||
let title = window_snapshot.title.trim();
|
||||
!title.is_empty()
|
||||
@@ -843,7 +867,13 @@ fn handle_key_press(app: &mut App, key_code: KeyCode) -> Result<()> {
|
||||
}
|
||||
|
||||
fn update_session_stats(app: &mut App) -> window::WindowSnapshot {
|
||||
let window_snapshot = window::get_snapshot();
|
||||
update_session_stats_with_snapshot(app, window::get_snapshot())
|
||||
}
|
||||
|
||||
fn update_session_stats_with_snapshot(
|
||||
app: &mut App,
|
||||
window_snapshot: window::WindowSnapshot,
|
||||
) -> window::WindowSnapshot {
|
||||
app.evidence_health = window_snapshot.health.clone();
|
||||
let window_title = if app.state == State::Paused {
|
||||
constants::PAUSED.to_string()
|
||||
@@ -1424,7 +1454,8 @@ mod tests {
|
||||
|
||||
assert_eq!(app.state, State::InProgress);
|
||||
assert!(app.is_unknown_window_acknowledged(&prompted_unknown));
|
||||
assert!(!app.is_unknown_window_acknowledged(&dismissal_foreground));
|
||||
assert!(app.is_unknown_window_acknowledged(&dismissal_foreground));
|
||||
assert!(app.is_unknown_window_acknowledged(&prompted_unknown));
|
||||
|
||||
let records = EventLog::open(&path).unwrap().records().unwrap();
|
||||
let violation = records
|
||||
@@ -1451,6 +1482,78 @@ mod tests {
|
||||
let _ = fs::remove_file(path);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn post_dismissal_app_foreground_does_not_prompt_or_clear_triggering_acknowledgement() {
|
||||
let path = unique_event_log_path("violation-prompt-app-handoff");
|
||||
let mut app = App::new_with_event_log_path(&path).unwrap();
|
||||
let prompted_unknown = window::WindowSnapshot {
|
||||
title: "Browser - unrelated".to_string(),
|
||||
class: Some("firefox".to_string()),
|
||||
health: antidrift::domain::EvidenceHealth::Available,
|
||||
};
|
||||
let dismissal_foreground = window::WindowSnapshot {
|
||||
title: "AntiDrift".to_string(),
|
||||
class: Some("terminal".to_string()),
|
||||
health: antidrift::domain::EvidenceHealth::Available,
|
||||
};
|
||||
let different_unknown = window::WindowSnapshot {
|
||||
title: "Chat - unrelated".to_string(),
|
||||
class: Some("chat".to_string()),
|
||||
health: antidrift::domain::EvidenceHealth::Available,
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
let commitment_id = app
|
||||
.session_controller
|
||||
.active_commitment()
|
||||
.unwrap()
|
||||
.id
|
||||
.clone();
|
||||
let mut log = EventLog::open(&path).unwrap();
|
||||
log.append(
|
||||
EventType::PolicyApplied,
|
||||
RuntimeState::Active,
|
||||
Some(commitment_id.clone()),
|
||||
serde_json::json!({
|
||||
"schema_version": antidrift::domain::POLICY_SCHEMA_VERSION,
|
||||
"policy_id": "policy-app-handoff-test",
|
||||
"commitment_id": commitment_id,
|
||||
"runtime_state": "active",
|
||||
"enforcement_level": "warn",
|
||||
"allowed_context": {
|
||||
"window_classes": [],
|
||||
"window_title_substrings": ["Editor"],
|
||||
"domains": [],
|
||||
"repos": [],
|
||||
"commands": [],
|
||||
},
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut app = App::new_with_event_log_path(&path).unwrap();
|
||||
app.enter_violation_prompt(&prompted_unknown);
|
||||
app.violation_dismissal_reason = "Needed to inspect docs".to_string();
|
||||
app.dismiss_violation_if_valid().unwrap();
|
||||
|
||||
app.tick_active_session_with_snapshot(dismissal_foreground)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(app.state, State::InProgress);
|
||||
assert!(app.is_unknown_window_acknowledged(&prompted_unknown));
|
||||
|
||||
app.tick_active_session_with_snapshot(different_unknown)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(app.state, State::ViolationPrompt);
|
||||
|
||||
let _ = fs::remove_file(path);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rating_completion_returns_controller_to_planning_for_next_session() {
|
||||
let path = unique_event_log_path("rating-completion");
|
||||
|
||||
Reference in New Issue
Block a user