# Design — Option 1 « Terminal + MCP » (orchestration inter-agents)

> Décision produit arbitrée (2026-06-11). Remplace la vue chat structurée par le
> terminal natif + délégation inter-agents par outils MCP. Source : agent Architecte.
> Statut : **design validé, dev NON commencé** (limite de session atteinte le 2026-06-11,
> reset 3:40am Europe/Paris). Reprendre par les lots backend B-0→B-5 et frontend F-1.

## Objectif
- **Vue humaine = terminal brut natif** (PTY interactif). Réflexion live + Échap = natifs CLI, zéro parsing par modèle. On abandonne `AgentChatView`/stream-json comme vue.
- **Délégation cross-model via MCP** : `idea_ask_agent(target, task)` bloquant → la cible traite quand libre (FIFO) → rend son résultat via NOUVEL outil `idea_reply(result)` → IdeA débloque l'appelant. Fin-de-tour = signal MCP explicite.
- Principes : 1 agent = 1 employé (1 process/session, input FIFO) ; hexagonal + SOLID stricts ; plus aucun `parse_event` requis pour vue ni orchestration.

## Découvertes clés de l'architecte (état réel du code)
1. La **file FIFO existe déjà** : `OrchestratorService` (`crates/application/src/orchestrator/service.rs`) a `ask_locks: Mutex<HashMap<AgentId, Arc<AsyncMutex<()>>>>` + `ask_lock_for()` + `ASK_QUEUE_WAIT_CAP` (600s) + `ASK_AGENT_TIMEOUT` (300s). On la formalise en port `AgentMailbox` (pour porter un `oneshot` de réponse).
2. `idea_ask_agent` → `agent.message` → `OrchestratorCommand::AskAgent{target_agent, task}` **déjà câblé** (mcp/tools.rs, domain/orchestrator.rs, service.rs). On réimplémente le **corps** de `ask_agent()`.
3. Aujourd'hui `ask_agent` **exige une session structurée** et renvoie `AppError::Invalid` si la cible est en PTY brut (service.rs ~400-410). **Inverser cette branche** : PTY vivant = canal normal.
4. Routage structuré dans `crates/application/src/agent/lifecycle.rs` (`LaunchAgent` ~1100). Levier de bascule : **ne plus injecter la fabrique structurée au composition root** (`crates/app-tauri/src/state.rs`, `with_structured`).
5. `apply_mcp_config` (lifecycle.rs ~1391) écrit déjà `.mcp.json` + `--mcp-config` AVANT le spawn, **chemin PTY inclus** → la CLI PTY a déjà le serveur MCP IdeA (à vérifier par test B-0). Vigilance : `ensure_mcp_server` doit piloter `McpServer::serve` sur le loopback.
6. `idea_reply` n'existe nulle part : seul vrai ajout de surface.

## Lots BACKEND (Rust — agent dev backend) ; NE PAS faire B-6 (nettoyage) avant coordination
- **B-0** Prérequis transport MCP : garantir CLI PTY reçoit `--mcp-config <path>` (endpoint/project/requester) + `serve` piloté loopback. Test : CLI factice PTY appelle `idea_list_agents`, reçoit réponse.
- **B-1** Port `AgentMailbox` + `InMemoryMailbox`. Domaine pur (`crates/domain/src/mailbox.rs` ou ports.rs) : trait + `Ticket{id,requester,task}`, `TicketId`, `MailboxError`. Infra (`crates/infrastructure/src/mailbox/`) : `HashMap<AgentId, VecDeque<(Ticket, oneshot::Sender<String>)>>` + mutex ; `enqueue` rend `PendingReply` (sur `oneshot::Receiver`). Tests : FIFO ; `resolve` réveille le bon pending ; 2 ask même cible sérialisés ; cibles ≠ non bloquants ; timeout retire ticket de tête.
- **B-2** Bascule routage : tous en PTY. `state.rs` : retirer `with_structured` de `LaunchAgent`/`OrchestratorService`/`ChangeAgentProfile`. Tests : profil Claude → PTY ; DTO renvoie `CellKind::Pty`. Ne pas supprimer `launch_structured` (mort-code, nettoyage ultérieur).
- **B-3** Réimplémenter `ask_agent` : résoudre id → `mailbox.enqueue` → ticket en tête → garantir cible vivante PTY (sinon LaunchAgent PTY bg) → `PtyPort::write` préfixe `[IdeA · tâche de {A} · ticket {id}] {task}\n` → `await PendingReply` borné `ASK_AGENT_TIMEOUT`. PTY vivant = normal. Timeout : garder agent vivant, retirer ticket de tête. Publier `AgentReplied`. Injecter `Arc<dyn AgentMailbox>` + `Arc<dyn PtyPort>`. Tests : injection bon handle ; agent mort relancé ; timeout libère file ; AgentReplied.
- **B-4** Outil/action `idea_reply` : `ToolDef idea_reply` (schéma `{result:string}` seul, pas de ticket_id exposé), action wire `agent.reply`, `OrchestratorCommand::Reply{from:AgentId, result}`, `validate`, `map_tool_call` (passe `requester` du handshake comme `from`), bras dispatch → `mailbox.resolve(from, result)`. Corrélation implicite : `idea_reply` résout le ticket en tête de la file de l'émetteur (identité via handshake, pas via id géré par le modèle). `tool_returns_reply` : idea_reply = ACK sans inline. Tests : mapping ; validate exige result ; resolve corrèle tête ; reply sans ask = erreur typée (pas de panic).
- **B-5** Protocole délégation dans le contexte : injecter dans convention file (`apply_injection`) + description outil : « reçois `[IdeA · tâche …]` → traite → appelle IMPÉRATIVEMENT `idea_reply(result=…)` ; ne réponds jamais qu'en texte. » Test : convention file contient l'instruction.

## Lots FRONTEND (TS/React — agent dev frontend) ; NE PAS faire F-2 (suppression) avant coordination
- **F-1** Router toute cellule agent vers `TerminalView` (jamais `AgentChatView`) ; ré-attache PTY + scrollback OK. Backend renverra `cellKind:"pty"`. Lire `frontend/src/features/layout/LayoutGrid.tsx`, `features/chat/AgentChatView.tsx`, `TerminalView`, `adapters/agent.ts`, `ports/index.ts`, `domain/index.ts`. Laisser `AgentChatView` inerte (non monté), pas supprimé. Tests Vitest : agent rend `TerminalView`, jamais `AgentChatView` ; re-mount repeint pty.

## Ordre / dépendances
```
B-0 ─┬─ B-2 ─┬─ B-3 ─ B-4 ─ B-5
B-1 ─┘       └─ F-1
Nettoyage (B-6, F-2) en dernier, coordonné.
```
Chemin critique : B-0 → B-2 → B-3 → B-4 → B-5. B-1 ∥ B-0. F-1 dès B-2.

## Cohérence
Domaine sans I/O (port + entités pures) ; oneshot/PTY/MCP = infra ; application via ports. Open/Closed (idea_reply = ajout, dispatch intact) ; Liskov (Claude/Codex identiques derrière PTY+MCP) ; 1 process/agent préservé.

## Fichiers à toucher
- Domaine : `mailbox.rs` (nouveau) / `ports.rs` ; `orchestrator.rs` (variante `Reply` + action `agent.reply`).
- Application : `orchestrator/service.rs` (ask_agent + reply + injection ports) ; `agent/structured.rs` (supprimé au nettoyage) ; `agent/lifecycle.rs` (routage).
- Infra : `mailbox/` (nouveau) ; `orchestrator/mcp/tools.rs` (idea_reply) ; `orchestrator/mcp/server.rs` (passer requester).
- app-tauri : `state.rs` (retrait with_structured + injection mailbox + ensure_mcp_server) ; `commands.rs`/`dto.rs` (nettoyage ultérieur).
- Frontend : `features/layout/LayoutGrid.tsx` (routage TerminalView) ; `features/chat/*` (nettoyage ultérieur).
