feat(infra): temp IdeA généralisé à tous les agents + purge du store managed-tmp au shutdown d'IdeA — #200 (QA verte)

This commit is contained in:
2026-08-15 00:14:25 +02:00
parent e6f6eb5b1b
commit b5653b24b5
10 changed files with 271 additions and 14 deletions

View File

@ -499,7 +499,7 @@ async fn app_exit_work_guard_state(
///
/// The order intentionally mirrors the historical inline `CloseRequested` hook:
/// snapshot windows, snapshot `agent_was_running`, kill PTYs, stop model servers,
/// stop the embedded server, then close secondary webview windows.
/// stop the embedded server, purge managed temp, then close secondary webview windows.
pub fn shutdown_app_after_confirm(handle: &tauri::AppHandle) {
if let Some(state) = handle.try_state::<AppState>() {
let open_windows = snapshot_open_webview_windows(handle);
@ -508,6 +508,7 @@ pub fn shutdown_app_after_confirm(handle: &tauri::AppHandle) {
let snapshot = std::sync::Arc::clone(&state.snapshot_running_agents);
let model_servers = std::sync::Arc::clone(&state.ensure_local_model_server);
let embedded_server = std::sync::Arc::clone(&state.embedded_server);
let purge_managed_temp = std::sync::Arc::clone(&state.purge_managed_temp);
let open_projects = state.open_project_ids();
let handles = state.terminal_sessions.handles();
tauri::async_runtime::block_on(async move {
@ -526,12 +527,19 @@ pub fn shutdown_app_after_confirm(handle: &tauri::AppHandle) {
}
let _ = model_servers.stop_on_app_exit().await;
let _ = embedded_server.stop().await;
purge_managed_temp_for_shutdown(&purge_managed_temp).await;
});
}
close_non_main_webview_windows(handle);
}
async fn purge_managed_temp_for_shutdown(purge: &application::PurgeManagedTemp) {
if let Err(err) = purge.execute().await {
application::diag!("[managed-temp] shutdown purge failed: {err}");
}
}
pub(crate) fn confirm_next_main_window_close() {
EXIT_GUARD_CONFIRMED.store(true, Ordering::SeqCst);
}
@ -848,12 +856,21 @@ mod tests {
use super::{
apply_main_close_decision, confirm_next_main_window_close, consume_exit_guard_confirmation,
decide_main_close_action, persisted_view_identity_from_label,
persisted_window_identity_from_label, should_install_exit_guard, MainCloseAction,
persisted_window_identity_from_label, purge_managed_temp_for_shutdown,
should_install_exit_guard, MainCloseAction,
};
use super::{should_close_with_main_window, PersistedWindowKind};
use application::AppExitWorkGuardState;
use application::{AppExitWorkGuardState, PurgeManagedTemp};
use async_trait::async_trait;
use domain::ports::ManagedTempStore;
use domain::{
ManagedTempAllocation, ManagedTempError, ManagedTempGcReport, ManagedTempHandle,
ManagedTempLease,
};
use serde_json::json;
use std::cell::Cell;
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use tauri::test::{get_ipc_response, mock_builder, mock_context, noop_assets, INVOKE_KEY};
@ -865,6 +882,57 @@ mod tests {
);
}
struct FailingPurgeStore {
called: Arc<AtomicBool>,
}
#[async_trait]
impl ManagedTempStore for FailingPurgeStore {
async fn allocate(
&self,
_allocation: ManagedTempAllocation,
) -> Result<ManagedTempLease, ManagedTempError> {
Err(ManagedTempError::Store("unused".to_owned()))
}
async fn resolve(
&self,
_handle: &ManagedTempHandle,
_now_ms: u64,
) -> Result<ManagedTempLease, ManagedTempError> {
Err(ManagedTempError::Store("unused".to_owned()))
}
async fn release(
&self,
_handle: &ManagedTempHandle,
_now_ms: u64,
) -> Result<ManagedTempLease, ManagedTempError> {
Err(ManagedTempError::Store("unused".to_owned()))
}
async fn gc(&self, _now_ms: u64) -> Result<ManagedTempGcReport, ManagedTempError> {
Err(ManagedTempError::Store("unused".to_owned()))
}
async fn purge_all(&self) -> Result<ManagedTempGcReport, ManagedTempError> {
self.called.store(true, AtomicOrdering::SeqCst);
Err(ManagedTempError::Store("partial purge failed".to_owned()))
}
}
#[tokio::test]
async fn shutdown_managed_temp_purge_is_attempted_and_non_blocking_on_error() {
let called = Arc::new(AtomicBool::new(false));
let purge = PurgeManagedTemp::new(Arc::new(FailingPurgeStore {
called: Arc::clone(&called),
}));
purge_managed_temp_for_shutdown(&purge).await;
assert!(called.load(AtomicOrdering::SeqCst));
}
#[test]
fn main_close_with_work_prevents_and_notifies_before_shutdown() {
assert_eq!(

View File

@ -232,6 +232,12 @@ mod tests {
async fn gc(&self, _now_ms: u64) -> Result<ManagedTempGcReport, ManagedTempError> {
Ok(ManagedTempGcReport { deleted: 1 })
}
async fn purge_all(&self) -> Result<ManagedTempGcReport, ManagedTempError> {
let deleted = self.leases.lock().unwrap().len();
self.leases.lock().unwrap().clear();
Ok(ManagedTempGcReport { deleted })
}
}
fn project() -> Project {