--- name: inter-agent-announcements-feature-and-codex-final-bug description: memory note inter-agent-announcements-feature-and-codex-final-bug metadata: type: project --- # Annonces inter-agent (UI live) + fix du Final Codex — DESIGN FIGÉ **Type :** design de feature + bug racine. Validé utilisateur le 2026-07-02, cadré par Architect. ## Bug racine (Codex) `crates/infrastructure/src/session/codex.rs` : `parse_event` (~l.86) mappe CHAQUE `item.completed{item.type=="agent_message"}` vers `ReplyEvent::Final`, et `send` (~l.234) casse au PREMIER Final (`break 'lines`). Or `codex exec --json` émet plusieurs `agent_message` par tour (préambule → outils → conclusion → `turn.completed`). IdeA renvoyait donc le **préambule** au demandeur au lieu de la **conclusion**. Preuve live : Architect répondait « je vais interroger QA… » au lieu du résultat. **Défaut d'implémentation, pas du headless Codex** (un `codex exec` brut déroule tout le tour). ## Feature validée (le bug devient fonctionnalité) - **Final** = DERNIER `agent_message` avant `turn.completed` → seule chose renvoyée au demandeur (résout le ticket). Bords : 1 seul message (préambule=conclusion) ; 0 message → `TargetReturnedNoReply` (inchangé). - **Annonces** = `agent_message` intermédiaires → nouvel événement NON terminal `ReplyEvent::Announcement{text}`, poussé sur le bus vers l'UI, JAMAIS renvoyé au demandeur, JAMAIS persisté au transcript (éphémère). - **UI** : (a) cellule DEMANDEUR = petit cadre preview près de la drop-list d'agents, filtré par `ticket_id` ; (b) cellule CIBLE si visible = overlay d'indisponibilité (texte haut « un agent est en train de lui parler » + annonces défilantes au centre), monté sur live-state Working, retiré au Final/idle (reste tant qu'≥1 ticket actif sur la cible). ## Contrat (Architect) - `ReplyEvent::Announcement{text}` (non terminal) vs `Final{text}` (terminal, résout). - `drain_with_readiness` : Announcement → publie event, ne résout pas, ne passe pas idle ; Final → résout ticket + idle + fin du drain. - Nouveau `DomainEvent::AgentAnnouncement{project_id, requester, target, ticket_id, text, at_ms}` (attribution obligatoire pour router vers la bonne cellule + isoler projet/onglet). - Overlay cible = état UI piloté par live-state (pas un blocage du PTY : le contact passe par session headless séparée, `allow_structured_alongside_pty:true`). - Généralisation Claude : `session/claude.rs` garde son terminal explicite (`result`) comme Final ; messages complets non terminaux → Announcement ; NE PAS promouvoir les `TextDelta` tokenisés (bruit) ; tool events restent `ToolActivity`. ## Découpage dev (Architect) - **B1** domaine/appli : `ReplyEvent::Announcement`, `DomainEvent::AgentAnnouncement`, `structured.rs`, `service.rs` (`ask_structured` ne termine que sur Final). - **B2** Codex `codex.rs` : bufferiser le dernier `agent_message`, émettre les précédents en Announcement, le dernier en Final à `turn.completed`. - **B3** Tauri `app-tauri/{events,dto,state}.rs` : relay + DTO camelCase. - **F1** front store/gateway annonces (index par ticket/target, borné ~20-50). - **F2** front preview cellule demandeur. - **F3** front overlay cellule cible. - **Q** QA e2e : Main→Architect→QA, préambule+outil+conclusion ; Main ne reçoit que la conclusion ; preview demandeur + overlay cible pendant le travail ; fermeture au Final. Critère : reproduire le bug initial et constater la correction. ## État Design figé, cadrage Architect complet. Reste : Git branche → B1/B2/B3 + F1/F2/F3 → QA. Non démarré côté dev au 2026-07-02. Liens : [[inter-agent-live-context-shared-per-agent]], [[rendezvous-no-reply-backstop-design]], [[headless-interagent-conversation-objective]], [[conversation-viewer-ls7-frontend]].