# Ticket #13 — Lot B0/F0 : inventaire du transport frontend (TS/React) > Livrable d'inventaire. **Aucun code de feature modifié.** Miroir frontend de > l'inventaire backend (`ticket13-b0-backend-transport-inventory.md`). > Objectif : cartographier tous les accès `@tauri-apps/api`, les classer par nature > (request/response · stream · event · desktop-only), et localiser la frontière > « gateways TS transport-neutres » du lot F1 (adapter Tauri desktop | adapter > HTTP+WS web). ## 0. Constat majeur — la frontière F1 existe déjà (à ~90 %) Le frontend est **déjà** structuré exactement comme le plan cible le demande : - **Ports transport-neutres** : `src/ports/index.ts` déclare 21 gateways (interfaces TS) décrivant *ce dont l'UI a besoin*, sans aucune référence à Tauri. Toute la couche `features/` + `app/` dépend de ces ports via DI (`useGateways()`). - **Adapters = seul lieu Tauri** : `src/adapters/*` implémente les ports via `@tauri-apps/api`. C'est le **seul** répertoire autorisé à importer Tauri. - **Garde d'architecture L1 active** : `src/app/no-direct-invoke.test.ts` échoue en CI si un fichier hors `src/adapters` importe `@tauri-apps/api` **ou** appelle `invoke(`. Cette garde est notre filet de sécurité pour F1 : elle garantit qu'aucun composant n'appelle Tauri en dur — donc **aucun composant métier à réécrire** pour le web. - **Composition déjà bifurquée** : `src/app/di.tsx` → `resolveGateways()` choisit `createTauriGateways()` vs `createMockGateways()` selon `VITE_USE_MOCK`. F1 ajoute simplement une **3ᵉ implémentation** (`createHttpWsGateways()`) au même seam. **Conséquence sur le plan de lots** : F1 n'est *pas* une extraction de frontière (elle est faite), mais l'écriture d'un **second jeu d'adapters** derrière des ports inchangés. Le risque est concentré dans **3 adapters à flux (Channel)** et **3 usages desktop-only**, pas dans les 15 gateways request/response (mécaniques). ## 1. Inventaire exhaustif des accès `@tauri-apps/api` Aucun accès hors `src/adapters`. Détail par fichier adapter : | Adapter | API Tauri utilisée | Nature | Commentaire transport | |---|---|---|---| | `system.ts` | `invoke` (`health`) | request/response | trivial HTTP | | `system.ts` | `listen("domain://event")` | **event** | bus d'événements domaine global → **WS** (fan-out serveur→client) | | `system.ts` | `open()` de `@tauri-apps/plugin-dialog` | **desktop-only** | picker dossier natif — **pas de sens sur web** (voir §4) | | `agent.ts` | `invoke` (list/create/change/read/update/delete/…) | request/response | HTTP | | `agent.ts` | `Channel` (`launch_agent`, `reattach_terminal`) | **stream** | flux PTY agent → **WS** (contrat PTY du carnet) | | `terminal.ts` | `Channel` (`open_terminal`, `reattach_terminal`) | **stream** | flux PTY terminal → **WS** (cœur de F3) | | `terminal.ts` | `invoke` (`write_/resize_/close_terminal`) | request/response (contrôle PTY) | messages WS client→serveur (input/resize/close) | | `ticket.ts` | `Channel` (`agent_send`) | **stream** | flux de réponse chat assistant ticket → **WS** | | `ticket.ts` | `invoke` (~20 commandes `ticket_*`) | request/response | HTTP | | `window.ts` | `invoke` (`open_/close_/list_view_window`) | **desktop-only** | cycle de vie fenêtre OS (WebviewWindow) — voir §4 | | `window.ts` | `listen("view-window://lifecycle")` | **desktop-only / event** | fermeture fenêtre OS — voir §4 | | `focusedProject.ts` | `invoke` + `listen("focused-project://changed")` | **desktop-only / event** | canal fenêtre principale ⇄ fenêtre détachée — voir §4 | | `project.ts`, `layout.ts`, `git.ts`, `profile.ts`, `modelServer.ts`, `template.ts`, `skill.ts`, `memory.ts`, `embedder.ts`, `permission.ts`, `workState.ts`, `conversation.ts`, `input.ts` | `invoke` seul | request/response | HTTP mécanique | | `uiPreferences.ts` | *(aucune)* — `localStorage` | frontend-pur | déjà transport-neutre, marche tel quel sur web | ## 2. Classement par nature de transport ### 2a. `request/response` (le gros du volume — mapping HTTP direct) Tous les `invoke()` non-Channel. 15 gateways sur 21 sont **exclusivement** request/response : `project, layout, git, profile, modelServer, template, skill, memory, embedder, permission, workState, conversation, input`, + le `health` de `system` et les ~20 commandes `ticket_*`. → **Aucune décision d'archi** : un adapter HTTP générique (POST `{command, args}` ou route par commande) suffit. C'est le socle du **premier livrable B4/F2** (web ouvre un projet + read-only), qui n'a besoin d'aucun stream. ### 2b. `stream` (Channel Tauri → WebSocket) — **le vrai chantier F1/F3** Trois flux, tous modélisés côté port par un **callback `onData`/`onChunk`** (le port ne connaît jamais `Channel`) : 1. **PTY terminal** — `terminal.ts` `openTerminal`/`reattach` : `Channel`, octets bruts → `term.write`. **Cœur de F3 (xterm sur WS).** 2. **PTY agent** — `agent.ts` `launchAgent`/`reattach` : même `Channel`, réutilise `makeTerminalHandle`. Même transport que le PTY terminal (le carnet dit « agents via WS PTY », lot B6). 3. **Chat assistant ticket** — `ticket.ts` `sendTicketChat` : `Channel`, flux de réponse LLM structuré (`agent_send`). Stream applicatif, pas PTY. Point de contrat clé : le **handle** (`TerminalHandle`) porte déjà `write/resize/ detach/close` **et** la sémantique detach≠close (détacher la vue sans tuer le PTY). Cette sémantique **correspond exactement** au contrat PTY WebSocket du carnet (`attach_terminal`/`detach`, PTY autorité serveur, scrollback borné). L'adapter WS implémentera `makeTerminalHandle` en émettant `input/resize/detach/close` sur la socket et en poussant `output(seq,bytes)` dans `onData`. **Le port n'a pas à changer.** ### 2c. `event` (listen Tauri → WebSocket serveur→client) Un seul événement *portable* : `system.ts` `onDomainEvent("domain://event")` — le bus d'événements domaine (agent launched, rate-limited, backgroundTaskChanged, issueDeleted, progress modèle…). Sur web ⇒ **canal WS serveur→client** (ou multiplexé sur la même socket que le PTY selon l'arbitrage HTTP+WS séparés vs WS unique, laissé à Architect). Les deux autres `listen` (`view-window://lifecycle`, `focused-project://changed`) sont **desktop-only** (§4), pas des events métier. ### 2d. `desktop-only` (pas de portage web direct — voir §4) `system.pickFolder` (dialog natif), tout `window.ts` (WebviewWindow OS), `focusedProject.ts` (coordination multi-fenêtres OS). ## 3. Frontière F1 : où placer les deux implémentations **La frontière est déjà `src/ports/` ⇄ `src/adapters/`.** F1 = ajouter un dossier `src/adapters/http/` (ou `webws/`) avec une implémentation par port, et un `createHttpWsGateways()` branché dans `resolveGateways()`. Découpage recommandé des adapters web par priorité (aligné sur les lots du carnet) : - **F2 (premier livrable, request/response only)** : adapters HTTP pour `system.health`, `project`, `layout` (read), `workState`, `ticket` (read), `agent.listAgents` + `onDomainEvent` sur WS. Suffit pour « ouvrir un projet + état read-only ». - **F3 (xterm sur WS)** : adapter WS pour `terminal` (+ réécriture de `makeTerminalHandle` en variante socket). `TerminalView.tsx` **ne change pas** : il ne connaît que le port et un `open/reattach` injecté. - **B6/F(agents)** : `agent.launchAgent/reattach` sur le même transport WS PTY. - **Chat ticket** : `ticket.sendTicketChat` sur WS applicatif. - **desktop-only** : `window`, `focusedProject`, `pickFolder` → implémentations web dégradées/alternatives (§4). **Aucun composant métier (`features/`, `app/`) n'appelle Tauri** — garanti par la garde L1. Donc F1 ne touche **que** `src/adapters/`, `src/app/di.tsx` (3-way resolve) et `vite`/build (cible web sans Tauri). C'est la bonne nouvelle du plan. ## 4. Points desktop-only — décisions à cadrer (Architect) Ces trois surfaces n'ont pas d'équivalent WS trivial ; elles ne bloquent **pas** le premier livrable (read-only) mais doivent être tranchées avant les lots concernés : 1. **`system.pickFolder`** (créer/ouvrir un projet par un dossier local). Sur le serveur, le « dossier » est **sur la machine serveur**, pas sur le client web. ⇒ Il faut un **file-picker serveur** (browse arborescence serveur via HTTP) au lieu du dialog natif OS. Impacte la création/ouverture de projet côté web. **À cadrer.** 2. **`window.ts` (détacher un panneau en fenêtre OS)** : concept purement desktop (Tauri `WebviewWindow`). Équivalent web = nouvel **onglet/`window.open`** navigateur, sémantique différente (pas de registre anti-doublon OS). V1 web : dégrader en **no-op** ou en onglet navigateur, la garde L1 permet une impl web distincte sans toucher les composants. **À cadrer (probablement hors-scope V1 web).** 3. **`focusedProject.ts`** : coordination fenêtre principale ⇄ fenêtres détachées. Sans fenêtres détachées (point 2), le canal se réduit à un état local. V1 web : implémentation triviale in-memory / `BroadcastChannel` si multi-onglets. **À cadrer.** ## 5. Signaux pour le plan de lots / contrat de transport - ✅ **Rien ne remet en cause le plan.** La frontière gateways transport-neutres du carnet est **déjà réalisée et testée** (garde L1). F1 est un ajout d'adapters, pas un refactor de composants. Gain de risque majeur. - ✅ **Le contrat PTY WebSocket du carnet colle au port existant** : `TerminalHandle` (write/resize/detach/close, detach≠close, scrollback au reattach) mappe 1-pour-1 sur `attach_terminal/input/resize/detach/close` + `attached(scrollback)/output(seq)`. Aucune évolution de port nécessaire pour F3. - ⚠️ **3 flux Channel** (PTY terminal, PTY agent, chat ticket) partagent la même mécanique `onData`/callback — un **transport WS commun** peut les servir tous les trois. À confirmer avec le choix « HTTP+WS séparés vs WS multiplexé » (délégué Architect). - ⚠️ **`pickFolder`** est le seul point request/response qui **change de sémantique** sur web (dossier serveur ≠ dossier client). À arbitrer avant le lot création/ouverture de projet web. - ⚠️ **`window`/`focusedProject`** (multi-fenêtres OS) : probablement **hors V1 web** ; à confirmer pour ne pas gonfler le périmètre. - ℹ️ **`uiPreferences`** (localStorage) marche déjà tel quel sur web — aucun adapter à écrire. - ℹ️ **Convention DTO** : commandes snake_case, payloads camelCase, souvent enveloppés dans `{ request: {...} }`. L'adapter HTTP doit préserver cette enveloppe pour parler au même cœur backend (le miroir backend confirme ce point de contrat).