---
name: f35-launch-status-done-config-crud-blocked
description: F35.1 UI statut de lancement du serveur local livrée+verte ; F35.2 config CRUD bloquée sur un DTO figé qui ne matche pas le wire réel + delete manquant.
metadata:
  type: project
---
Ticket #35 sur `feature/modeles-locaux`.

**F35.1 LIVRÉ (vert)** : événements `model_server_status_changed`/`agent_launch_failed` déjà exposés via `domain://event`. Consommés dans `useAgents` → `modelServerStatusByServer` (par serverId) + `launchFailureByAgent` (par agentId). Badge `ModelServerLaunchBadge` monté par ligne dans `AgentsPanel`, corrélé via `profile.opencode.localModelServerId`. Contrat réel ≠ DTO figé du ticket : `serverId` sur l'enveloppe (pas par état) ; états camelCase `notConfigured|probing|starting|ready{reused}|failed{code,message}` ; PAS de `baseURL`/`model` émis dans le statut. Je me suis aligné sur le wire réel.

**F35.2 BLOQUÉ — 3 gaps backend remontés à Main :**
1. (bloquant) `save_model_server`/`list_model_servers` transmettent le domaine `LocalModelServerConfig` en `serde(transparent)`, forme IMBRIQUÉE : `{id, kind:"llamaCpp", name, endpoint:{baseURL,port}, model:{id,label,path?,servedName}, binary?, args, autoStart, stopPolicy}`. Le DTO figé du ticket est plat et FAUX (kind "llamacpp", baseURL/port/modelPath/servedModelName/binaryPath à plat). Pire : `model` exige `id`+`label` non-vides (LocalModelRef::new) absents du DTO figé → payload figé rejeté. Besoin arbitrage Architect : (A) couche DTO plate app-tauri qui remplit model.id/label, ou (B) MAJ du DTO figé vers la forme imbriquée + sémantique de model.id/label.
2. Pas de commande `delete_model_server`.
3. (côté moi) créer `ModelServerGateway`/port/adapter une fois le contrat tranché.

Saisie simple de `localModelServerId` (F36) laissée en place, pas de régression. tsc propre ; suite 593/593.