fix(domain,application,infrastructure,backend,app-tauri,frontend): Stop demande agent préserve la conversation ouverte — arrêt sans fermeture/clôture du chat, états cohérents après cancel sur les 3 adapters de session, cellule et workstate synchronisés — #343 (QA verte)

This commit is contained in:
2026-09-13 10:03:36 +02:00
parent a1e8a6466f
commit c37638ff24
26 changed files with 1210 additions and 232 deletions

View File

@ -44,10 +44,10 @@ use domain::{Memory, MemoryIndexEntry, MemoryLink, MemorySlug};
use uuid::Uuid;
use application::{
CloseTerminal, CreateAgentFromScratch, CreateSkill, GetLiveStateLean, HarvestMemoryFromTurn,
LaunchAgent, ListAgents, LiveStateProvider, LiveStateReadProvider, OrchestratorService,
SessionLimitService, SpawnBackgroundCommand, TerminalSessions, UpdateAgentContext,
UpdateLiveState,
CancelAgentChat, CloseTerminal, CreateAgentFromScratch, CreateSkill, GetLiveStateLean,
HarvestMemoryFromTurn, LaunchAgent, ListAgents, LiveStateProvider, LiveStateReadProvider,
OrchestratorService, SessionLimitService, SpawnBackgroundCommand, TerminalSessions,
UpdateAgentContext, UpdateLiveState,
};
use domain::live_state::{LiveEntry, LiveState, WorkStatus};
use domain::ports::LiveStateStore;
@ -1428,7 +1428,11 @@ async fn stop_agent_closes_structured_runtime_and_is_idempotent() {
fx.pty.kills().is_empty(),
"structured stop must not kill PTY"
);
assert_eq!(*cancel_called.lock().unwrap(), 0);
assert_eq!(
*cancel_called.lock().unwrap(),
1,
"structured hard stop must cancel the live turn before shutdown"
);
assert_eq!(
*shutdown_called.lock().unwrap(),
1,
@ -1453,6 +1457,46 @@ async fn stop_agent_closes_structured_runtime_and_is_idempotent() {
assert_eq!(*shutdown_called.lock().unwrap(), 1);
}
#[tokio::test]
async fn cancel_chat_keeps_structured_runtime_and_execution_admission_open() {
let agent = scratch_agent(aid(1), "dev-backend", "agents/dev-backend.md");
let contexts = FakeContexts::with_agent(&agent, "# persona");
let fx = ask_fixture(contexts);
let cancel_called = Arc::new(Mutex::new(0_u32));
let shutdown_called = Arc::new(Mutex::new(0_u32));
let session_id = sid(9343);
let session = Arc::new(CompletionSession {
id: session_id,
agent: agent.id,
completions: fx.mailbox.completions(),
cancel_called: Some(Arc::clone(&cancel_called)),
shutdown_called: Some(Arc::clone(&shutdown_called)),
}) as Arc<dyn AgentSession>;
fx.structured
.insert_in_project(project_id(), session, agent.id, nid(9343));
fx.structured.begin_turn(&session_id).unwrap();
let output = CancelAgentChat::new(Arc::clone(&fx.structured))
.execute(session_id)
.await
.expect("turn cancel succeeds");
fx.service
.cancel_agent_turn_execution_children(&project(), agent.id)
.await
.expect("child cancellation succeeds");
assert_eq!(
output.outcome,
domain::ports::CancelCurrentTurnOutcome::Cancelled
);
assert_eq!(*cancel_called.lock().unwrap(), 1);
assert_eq!(*shutdown_called.lock().unwrap(), 0);
assert!(fx.structured.session(&session_id).is_some());
fx.service
.ensure_agent_execution_active(&project(), agent.id)
.expect("turn cancellation must not close #320 admission");
}
#[tokio::test]
async fn stop_agent_from_unrelated_agent_is_rejected() {
let agent = scratch_agent(aid(1), "dev-backend", "agents/dev-backend.md");
@ -2117,11 +2161,13 @@ impl AgentSession for CompletionSession {
)),
}
}
async fn cancel_current_turn(&self) -> Result<(), AgentSessionError> {
async fn cancel_current_turn(
&self,
) -> Result<domain::ports::CancelCurrentTurnOutcome, AgentSessionError> {
if let Some(cancel_called) = &self.cancel_called {
*cancel_called.lock().unwrap() += 1;
}
Ok(())
Ok(domain::ports::CancelCurrentTurnOutcome::Cancelled)
}
async fn shutdown(&self) -> Result<(), AgentSessionError> {
if let Some(shutdown_called) = &self.shutdown_called {
@ -4999,8 +5045,10 @@ impl AgentSession for RestartingStructuredSession {
Ok(())
}
async fn cancel_current_turn(&self) -> Result<(), AgentSessionError> {
Ok(())
async fn cancel_current_turn(
&self,
) -> Result<domain::ports::CancelCurrentTurnOutcome, AgentSessionError> {
Ok(domain::ports::CancelCurrentTurnOutcome::AlreadyCompleted)
}
}