feat(backend): guard de fermeture "travail en cours" (#83)

Expose l'état du guard de sortie applicative (GetAppExitWorkGuardState) :
agents busy + tâches d'arrière-plan actives à travers tous les projets
ouverts, avec détails compacts pour la popup de confirmation. Le handler
CloseRequested d'app-tauri interroge ce guard avant de laisser la fenêtre
se fermer, et respecte la confirmation explicite de l'utilisateur
(EXIT_GUARD_CONFIRMED) pour ne pas la redemander en boucle.

QA vert (backend + frontend).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 19:18:39 +02:00
parent 8509653e3c
commit 60f4b33e53
7 changed files with 755 additions and 109 deletions

View File

@ -21,30 +21,30 @@ use application::{
CreateTemplate, DeleteAgent, DeleteEmbedderProfile, DeleteIssue, DeleteLayout, DeleteMemory,
DeleteModelServer, DeleteProfile, DeleteSkill, DeleteSprint, DeleteTemplate,
DescribeEmbedderEngines, DetectAgentDrift, DetectProfiles, DismissEmbedderSuggestion,
EnsureLocalModelServer, FirstRunState, GetLiveStateLean, GetMemory, GetProjectPermissions,
GetProjectWorkState, GitBranches, GitCheckout, GitCommit, GitGraph, GitInit, GitLog, GitStage,
GitStatus, GitUnstage, HarvestMemoryFromTurn, HealthUseCase, InspectConversation, LaunchAgent,
LaunchAgentInput, LinkIssues, ListAgents, ListAgentsInput, ListDevices, ListEmbedderProfiles,
ListIssues, ListLayouts, ListMemories, ListModelServers, ListProfiles, ListProjects,
ListResumableAgents, ListSkills, ListSprints, ListTemplates, LiveAgentRegistry, LiveSessions,
LiveStateLeanProvider, LiveStateProvider, LiveStateReadProvider, LoadLayout, McpRuntime,
McpToolPermissionCatalogue, MoveTabToNewWindow, MutateLayout, OnnxModelView, OpenProject,
OpenTerminal, OpenTicketAssistant, OrchestratorService, PairAttemptLimiter, PairDevice,
PermissionProjectorRegistry, ProposeContext, ReadAgentContext, ReadContext,
ReadConversationPage, ReadIssue, ReadIssueCarnet, ReadMcpToolPermissions, ReadMemory,
ReadMemoryIndex, ReadProjectContext, ReadSkill, ReadTemplate, RecallMemory, ReconcileLayouts,
ReconcileLiveState, ReconcileLiveStateInput, RecordTurn, RecordTurnProvider, ReferenceProfiles,
RenameDevice, RenameLayout, RenameSprint, ReorderSprints, ResizeTerminal,
ResolveAgentPermissions, ResolveMemoryLinks, RestoreOpenWindows, RetryBackgroundTask,
RevokeAllDevices, RevokeDevice, RotateConversationLog, SaveEmbedderProfile, SaveModelServer,
SaveProfile, SessionLimitService, SetActiveLayout, SnapshotOpenWindows, SnapshotRunningAgents,
SpawnBackgroundCommand, StopLiveAgent, StructuredRoutingMode, StructuredSessions,
SuggestedThisSession, SyncAgentWithTemplate, TerminalSessions, TouchDevice,
UnassignSkillFromAgent, UnassignTicketFromSprint, UnlinkIssues, UpdateAgentContext,
UpdateAgentMcpToolPermissions, UpdateAgentPermissions, UpdateIssue, UpdateIssueCarnet,
UpdateLiveState, UpdateMemory, UpdateProjectContext, UpdateProjectMcpToolPermissions,
UpdateProjectPermissions, UpdateSkill, UpdateTemplate, WakeSessionProvider, WriteMemory,
WriteToTerminal, AGENT_MEMORY_RECALL_BUDGET,
EnsureLocalModelServer, FirstRunState, GetAppExitWorkGuardState, GetLiveStateLean, GetMemory,
GetProjectPermissions, GetProjectWorkState, GitBranches, GitCheckout, GitCommit, GitGraph,
GitInit, GitLog, GitStage, GitStatus, GitUnstage, HarvestMemoryFromTurn, HealthUseCase,
InspectConversation, LaunchAgent, LaunchAgentInput, LinkIssues, ListAgents, ListAgentsInput,
ListDevices, ListEmbedderProfiles, ListIssues, ListLayouts, ListMemories, ListModelServers,
ListProfiles, ListProjects, ListResumableAgents, ListSkills, ListSprints, ListTemplates,
LiveAgentRegistry, LiveSessions, LiveStateLeanProvider, LiveStateProvider,
LiveStateReadProvider, LoadLayout, McpRuntime, McpToolPermissionCatalogue, MoveTabToNewWindow,
MutateLayout, OnnxModelView, OpenProject, OpenTerminal, OpenTicketAssistant,
OrchestratorService, PairAttemptLimiter, PairDevice, PermissionProjectorRegistry,
ProposeContext, ReadAgentContext, ReadContext, ReadConversationPage, ReadIssue,
ReadIssueCarnet, ReadMcpToolPermissions, ReadMemory, ReadMemoryIndex, ReadProjectContext,
ReadSkill, ReadTemplate, RecallMemory, ReconcileLayouts, ReconcileLiveState,
ReconcileLiveStateInput, RecordTurn, RecordTurnProvider, ReferenceProfiles, RenameDevice,
RenameLayout, RenameSprint, ReorderSprints, ResizeTerminal, ResolveAgentPermissions,
ResolveMemoryLinks, RestoreOpenWindows, RetryBackgroundTask, RevokeAllDevices, RevokeDevice,
RotateConversationLog, SaveEmbedderProfile, SaveModelServer, SaveProfile, SessionLimitService,
SetActiveLayout, SnapshotOpenWindows, SnapshotRunningAgents, SpawnBackgroundCommand,
StopLiveAgent, StructuredRoutingMode, StructuredSessions, SuggestedThisSession,
SyncAgentWithTemplate, TerminalSessions, TouchDevice, UnassignSkillFromAgent,
UnassignTicketFromSprint, UnlinkIssues, UpdateAgentContext, UpdateAgentMcpToolPermissions,
UpdateAgentPermissions, UpdateIssue, UpdateIssueCarnet, UpdateLiveState, UpdateMemory,
UpdateProjectContext, UpdateProjectMcpToolPermissions, UpdateProjectPermissions, UpdateSkill,
UpdateTemplate, WakeSessionProvider, WriteMemory, WriteToTerminal, AGENT_MEMORY_RECALL_BUDGET,
};
use async_trait::async_trait;
use domain::ports::{
@ -1020,6 +1020,8 @@ pub struct BackendCore {
pub list_resumable_agents: Arc<ListResumableAgents>,
/// Read-only live/busy state for the project's manifest agents.
pub get_project_work_state: Arc<GetProjectWorkState>,
/// App-wide work-in-progress guard used before confirmed application exit.
pub get_app_exit_work_guard_state: Arc<GetAppExitWorkGuardState>,
/// Human paginated read of a conversation's full transcript (lot LS6).
pub read_conversation_page: Arc<ReadConversationPage>,
/// Best-effort log rotation, triggered off the hot path at thread resume/open (lot LS6).
@ -2233,6 +2235,9 @@ impl BackendCore {
)
.with_background_tasks(Arc::clone(&background_tasks_port)),
);
let get_app_exit_work_guard_state = Arc::new(GetAppExitWorkGuardState::new(Arc::clone(
&get_project_work_state,
)));
// Lot LS6 — rotation (hors chemin chaud) + lecture humaine paginée. Tous deux
// composent le provider d'archive par root ; la rotation lit aussi le handoff
// (plancher `up_to`, INV-LS6). Aucune persistance déclenchée par un `append`.
@ -2543,6 +2548,7 @@ impl BackendCore {
change_agent_profile,
list_resumable_agents,
get_project_work_state,
get_app_exit_work_guard_state,
read_conversation_page,
rotate_conversation_log,
attach_live_agent,