# Brief Dev — Lot D5 : frontend chat (§17.9)

> Demandé par **Main** à **DevFrontend** (dev) + **QA** (test). Cycle §3 : code → tests → vert.
> Périmètre **frontend uniquement** (`frontend/src`). Le backend D4 est livré et committé (`f4d5727`).

## 0. Où on en est

Le fil §17 (exécution structurée des agents IA) est livré jusqu'à **D4 inclus** côté backend :
les commandes Tauri `agent_send` / `reattach_agent_chat` / `close_agent_session` existent et
streament des `ReplyChunk` sur un `Channel`. D5 = **la vue chat React** qui consomme ça, plus
le **routage par type de cellule** dans le layout.

### Contrats backend exacts à mirrorer (déjà livrés)
Commandes Tauri (`crates/app-tauri/src/commands.rs`) :
- `agent_send(sessionId: string, prompt: string, onReply: Channel<ReplyChunk>) -> void`
- `reattach_agent_chat(sessionId: string, onReply: Channel<ReplyChunk>) -> ReattachChatDto`
- `close_agent_session(sessionId: string) -> void`

DTO (`crates/app-tauri/src/dto.rs`) — **sérialisation camelCase tagué `kind`** :
```ts
type ReplyChunk =
  | { kind: "textDelta"; text: string }
  | { kind: "toolActivity"; label: string }
  | { kind: "final"; content: string };

// ReattachChatDto : le scrollback de conversation (chunks déjà streamés)
interface ReattachChatDto { sessionId: string; scrollback: ReplyChunk[]; }

// Et surtout : le DTO de session porte désormais cellKind
type CellKind = "pty" | "chat";   // toujours présent sur TerminalSessionDto
```
> `cellKind` vaut `"chat"` quand l'agent est piloté en mode structuré (profil Claude/Codex),
> `"pty"` pour un terminal brut. C'est **la seule info dont le frontend a besoin** pour router
> cellule chat vs terminal.

## 1. Périmètre D5 (réf. tableau §17.9 ligne D5)

1. **`AgentChatView`** (nouveau, `frontend/src/features/chat/`) : vue de conversation IdeA.
   - Affiche les **deltas live** (accumulation `textDelta` → texte du tour en cours), l'**activité
     d'outil** (`toolActivity`), et **fige le tour** sur `final`.
   - Zone de **saisie** d'un prompt → appelle `AgentGateway.sendPrompt`.
   - **Scrollback de conversation** : à l'attache, repeint l'historique renvoyé par
     `reattachChat` ; survit à un changement d'onglet/layout (ré-attache, pas re-spawn).
   - C'est le **jumeau chat** de `TerminalView` (`frontend/src/features/terminals/TerminalView.tsx`,
     283 l.) — inspire-toi de sa gestion de cycle de vie (mount/attach/detach), mais pour un
     flux de messages structuré au lieu d'octets xterm.
2. **Routage par `cellKind` dans `LayoutGrid`** (`frontend/src/features/layout/LayoutGrid.tsx`,
   fonction `LeafView` ~l.159) : une cellule rend `AgentChatView` si `cellKind === "chat"`,
   sinon `TerminalView` (comportement actuel inchangé). Le `cellKind` arrive sur le handle/session
   au lancement (sortie de `launchAgent`) — propage-le jusqu'au leaf.
3. **Port `AgentGateway`** (`frontend/src/ports/index.ts`, interface ~l.76) : ajoute
   - `sendPrompt(sessionId: string, prompt: string, onReply: (c: ReplyChunk) => void): Promise<void>`
   - `reattachChat(sessionId: string, onReply: (c: ReplyChunk) => void): Promise<ReplyChunk[]>`
   - `closeAgentSession(sessionId: string): Promise<void>`
   (signatures à aligner avec le style des méthodes existantes `launchAgent`/`reattach`).
4. **Adapter Tauri** (`frontend/src/adapters/agent.ts`, classe `TauriAgentGateway`) : implémente
   les 3 méthodes via `invoke(...)` + `new Channel<ReplyChunk>()` (modèle déjà présent pour
   `launchAgent`/`reattach` qui utilisent `Channel<number[]>`).
5. **Mock gateway** (`frontend/src/adapters/mock/index.ts`) : streame des `ReplyChunk` scriptés
   (quelques `textDelta` puis un `final`) pour les tests et le dev hors-Tauri.
6. **Types TS** : ajoute `ReplyChunk`, `CellKind`, `ReattachChatDto` aux types partagés (là où
   vivent les autres mirrors de DTO), et le champ `cellKind` sur le type de session/handle.

## 2. Invariants à respecter

- **Zéro régression terminal** : `TerminalView` et le chemin PTY de `LayoutGrid` restent
  identiques ; une cellule `pty` se comporte exactement comme avant.
- **Ré-attache ≠ re-spawn** : changer d'onglet puis revenir repeint la conversation depuis le
  scrollback renvoyé par `reattachChat`, sans relancer de tour (miroir du PTY reattach).
- **Accumulation correcte** : les `textDelta` s'accumulent dans le tour courant ; `final` clôt
  le tour (le texte final fait foi). Pas de doublon delta/final affiché deux fois.
- Frontières : la vue dépend du **port** `AgentGateway`, jamais directement de `invoke`/Tauri
  (c'est l'adapter qui parle à Tauri). Hexagonal côté front respecté.

## 3. Tests attendus (QA — Vitest, réf. colonne « Tests attendus » D5 du §17.9)

Aligne-toi sur le style des `*.test.tsx` existants (`LayoutGrid.test.tsx`, `TerminalView.test.tsx`,
`adapters/agent.test.ts`), avec le **mock gateway** :
- cellule `cellKind:"chat"` rend `AgentChatView` ; `cellKind:"pty"` rend `TerminalView`.
- les `textDelta` s'accumulent à l'écran → `final` fige le tour.
- ré-attache repeint le scrollback **sans** re-spawn (le mock ne reçoit pas de nouveau `sendPrompt`).
- envoi d'un prompt → `AgentGateway.sendPrompt` appelé avec les bons args.
- `toolActivity` affiché comme activité d'outil.
- mock gateway streame bien une séquence `ReplyChunk` (deltas… puis final).

## 4. Méthode

Cycle §3 strict : DevFrontend code → QA écrit + exécute les tests Vitest → vert avant de clore.
Vérifie `npm run build` (tsc --noEmit + vite) **et** `npx vitest run` verts. **Ne pas committer,
ne pas push** — Main relit et commit. Rapport d'erreurs clair si rouge → correction → re-test.

## 5. Références

- Jumeau à copier : `frontend/src/features/terminals/TerminalView.tsx` (cycle de vie attach/detach).
- Routage cellule : `frontend/src/features/layout/LayoutGrid.tsx` (`LeafView`).
- Port : `frontend/src/ports/index.ts` (`AgentGateway`) ; adapter : `frontend/src/adapters/agent.ts` ;
  mock : `frontend/src/adapters/mock/index.ts`.
- Backend déjà livré : commit `f4d5727`, `crates/app-tauri/src/{chat,commands,dto}.rs`.
- Spec : `ARCHITECTURE.md` §17.6 (deux types de cellules) et tableau §17.9 ligne **D5**.
