fix(agents): enforcer l'invariant « 1 session vivante par agent » (singleton)
Un agent ne peut tourner que dans une seule cellule à la fois. La garde dans LaunchAgent refuse le spawn si l'agent est déjà vivant dans un autre node (AGENT_ALREADY_RUNNING) ; idempotent sur le même node ; le chemin resume (agent mort) reste inchangé. Le node_id est désormais plombé jusqu'au use case. Corrige le reset asymétrique d'une cellule au changement d'onglet : deux leaves partageant le même agent id rendaient session_for_agent/is_agent_live/stop_agent ambigus (cible arbitraire). Le churn reset/reattach déclenchait aussi les accents mélangés (FIFO intact, non touché). - snapshot agentWasRunning calculé par node (is_node_live) et non par agent - commande list_live_agents + live_agents()/node_for_agent()/is_node_live() - UI : dropdown grise les agents déjà placés ailleurs ; 2e cellule en doublon affiche « disponible » au lieu d'une relance fantôme Tests : cargo test (application + app-tauri) vert ; tsc + vitest vert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -9,6 +9,7 @@ use domain::ports::{
|
||||
EmbedderError, FsError, GitError, MemoryError, ProcessError, PtyError, RemoteError,
|
||||
RuntimeError, StoreError,
|
||||
};
|
||||
use domain::{AgentId, NodeId};
|
||||
|
||||
/// Errors surfaced by application use cases.
|
||||
///
|
||||
@ -44,6 +45,18 @@ pub enum AppError {
|
||||
#[error("remote error: {0}")]
|
||||
Remote(String),
|
||||
|
||||
/// An agent is already running in a live cell and cannot be launched again
|
||||
/// (the "one live session per agent" invariant). Reuse goes through
|
||||
/// templates (instantiate → distinct agents); IdeA never multi-instances a
|
||||
/// single agent.
|
||||
#[error("agent {agent_id} is already running in cell {node_id}")]
|
||||
AgentAlreadyRunning {
|
||||
/// The agent that is already live.
|
||||
agent_id: AgentId,
|
||||
/// The layout cell (leaf) currently hosting that agent's live session.
|
||||
node_id: NodeId,
|
||||
},
|
||||
|
||||
/// An unexpected internal error.
|
||||
#[error("internal error: {0}")]
|
||||
Internal(String),
|
||||
@ -62,6 +75,7 @@ impl AppError {
|
||||
Self::Process(_) => "PROCESS",
|
||||
Self::Git(_) => "GIT",
|
||||
Self::Remote(_) => "REMOTE",
|
||||
Self::AgentAlreadyRunning { .. } => "AGENT_ALREADY_RUNNING",
|
||||
Self::Internal(_) => "INTERNAL",
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user