fix(application,backend,app-tauri,frontend): réconcilie l'état idle et les Finals après Stop — #357 (QA verte)
Cause: après un cancel manuel de tour, le ticket médié busy/FIFO n'était pas
retiré (l'agent restait Busy, bouton Stop persistant) et un terminal tardif du
tour annulé pouvait bloquer le tour suivant (Final non affiché).
Backend: StructuredSessions lie chaque génération de tour à son ticket médié
(bind_turn_accounting) et request_turn_cancel restitue l'identité (agent,
ticket) exacte observée avant annulation; CancelAgentChat.with_turn_accounting
retire le ticket exact (cancel_head + mark_idle) seulement s'il est toujours
le ticket courant — jamais un successeur déjà remplacé;
StructuredHumanTurnGuard expose accounting_identity et agent_send (app-tauri)
lie la comptabilité au démarrage du tour humain.
Frontend: CustomAgentChatView réconcilie l'interaction active depuis le
work-state (agent idle sans délégation -> interaction terminée, fallback
« Tour terminé sans réponse finale. ») au lieu de laisser un agentBusyChanged
sans tour-id clore un tour plus récent; les terminaux tardifs (final/error) du
tour read-model-settled remplacent le fallback; tour suivant isolé des
callbacks tardifs de la génération précédente.
Tests: cancel_agent_chat (retrait du ticket exact avant le prompt suivant,
nettoyage tardif sans idle sur ticket successeur), CustomAgentChatView
(isolation tour suivant, réconciliation work-state + remplacement fallback par
Final tardif).
Réserve non bloquante: alignement fixture skills préexistant porté par le
commit test dédié 83659d9a.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1774,6 +1774,16 @@ impl StructuredHumanTurnGuard {
|
||||
}
|
||||
}
|
||||
|
||||
/// Exact mediated ticket owned by this logical human turn, if it acquired Busy.
|
||||
#[must_use]
|
||||
pub fn accounting_identity(
|
||||
&self,
|
||||
) -> Option<(domain::RuntimeAgentKey, domain::mailbox::TicketId)> {
|
||||
self.armed
|
||||
.load(std::sync::atomic::Ordering::Acquire)
|
||||
.then_some((self.agent, self.ticket_id))
|
||||
}
|
||||
|
||||
/// Resolves whether a concrete provider terminal event really ends the
|
||||
/// logical human turn. During self-restart it waits for the replacement
|
||||
/// session, then acknowledges the handoff so the scheduler can complete.
|
||||
@ -2757,7 +2767,6 @@ impl BackendCore {
|
||||
// `profile.structured_adapter`. Injectés dans LaunchAgent (routage §17.4) et
|
||||
// ChangeAgentProfile (shutdown polymorphe au hot-swap).
|
||||
let structured_sessions = Arc::new(StructuredSessions::new());
|
||||
let cancel_agent_chat = Arc::new(CancelAgentChat::new(Arc::clone(&structured_sessions)));
|
||||
let live_sessions = Arc::new(LiveSessions::new(
|
||||
Arc::clone(&terminal_sessions),
|
||||
Arc::clone(&structured_sessions),
|
||||
@ -3739,6 +3748,10 @@ impl BackendCore {
|
||||
});
|
||||
}
|
||||
let input_mediator = Arc::clone(&mediated_inbox) as Arc<dyn domain::input::InputMediator>;
|
||||
let cancel_agent_chat = Arc::new(
|
||||
CancelAgentChat::new(Arc::clone(&structured_sessions))
|
||||
.with_turn_accounting(Arc::clone(&input_mediator), Arc::clone(&mailbox)),
|
||||
);
|
||||
let background_tasks = Arc::new(AppBackgroundTaskStore::new(Arc::clone(&store_port)));
|
||||
let background_tasks_port = Arc::clone(&background_tasks) as Arc<dyn BackgroundTaskStore>;
|
||||
let (background_ready_tx, mut background_ready_rx) =
|
||||
|
||||
Reference in New Issue
Block a user