---
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]].