# Brief Dev — Lot D7 : menu de profils restreint + retrait custom (§17.9) > Demandé par **Main** à **DevBackend** + **DevFrontend** + **QA**. Cycle §3. > Dernier lot de §17. **Back + front.** DevBackend livre le contrat, DevFrontend consomme. ## 0. Objectif (§17.3 / §17.6) Tant que seuls Claude et Codex ont un adapter structuré, le **menu de sélection de profil IA** ne doit proposer **que** des profils pilotables en mode structuré. Conséquences : - **Gemini / Aider** (présents dans le catalogue de référence mais **sans** `structured_adapter`) ne sont **plus proposés** à la sélection. - Le **profil custom** est **retiré** (l'utilisateur ne peut plus saisir une commande arbitraire, car on ne saurait pas la piloter en structuré). Principe : `is_selectable(profile) == structured_adapter.is_some()` (équivaut à `AgentSessionFactory::supports(profile)`). C'est ce prédicat qui **filtre la liste exposée** (wizard first-run **et** création/édition d'agent). > Note : on **ne casse pas** le modèle `AgentProfile` (un profil sans adapter reste un profil > PTY/legacy valide, §17.3). On restreint seulement ce qui est **proposé à la sélection**. ## 1. Côté DevBackend (`crates/`) 1. **Prédicat de sélectionnabilité** centralisé : `is_selectable(&AgentProfile) -> bool` (= `structured_adapter.is_some()`). Place-le là où c'est cohérent (catalogue/usecases agent). Évite de dupliquer la logique ; si `AgentSessionFactory::supports` existe déjà (livré D2), garde la **même sémantique** (les deux doivent rester d'accord). 2. **Exposer uniquement les profils sélectionnables** au chemin de sélection : le use case qui alimente le wizard/la création (autour de `ReferenceProfiles` / `reference_profiles()` dans `crates/application/src/agent/{catalogue,usecases}.rs`) doit **filtrer** sur `is_selectable`. Gemini/Aider restent dans le catalogue **data** (ne les supprime pas du modèle) mais **n'apparaissent pas** dans la liste proposée. Décide proprement : soit un nouveau champ `selectable: bool` sur le DTO exposé, soit une liste déjà filtrée — choisis l'option la moins ambiguë pour le front et documente-la. 3. **Retrait custom (back)** : si une commande/usecase accepte un profil custom arbitraire pour la sélection/création depuis le wizard, neutralise ce chemin (ou documente qu'il n'est plus appelé). Ne casse pas la persistance de profils existants. 4. Vérifie `cargo build -p domain -p application -p app-tauri`. **Contrat à livrer à DevFrontend** (à mettre dans ton rapport) : la forme exacte de ce que le front reçoit (liste filtrée ? champ `selectable`/`structuredAdapter` sur `ProfileDto` ?) pour qu'il sache quoi afficher et quoi masquer. Rappel : `ProfileDto(pub AgentProfile)` sérialise déjà `structuredAdapter` (camelCase) — tu peux t'appuyer dessus plutôt que d'ajouter un champ. ## 2. Côté DevFrontend (`frontend/src/`) > **Ne démarre qu'après le contrat de DevBackend** (Main te relaiera la forme exacte). 1. **Wizard first-run** (`features/first-run/FirstRunWizard.tsx`, `ProfilesSettings.tsx`) : - n'affiche que les profils **sélectionnables** (Claude/Codex) ; - **retire le bloc `AddCustomProfile`** (`onAdd`/`vm.addCustom`, `emptyCustomProfile`, `aria-label="add custom profile"`) — le bouton/forme custom **disparaît**. 2. **Sélecteur d'agent** (création/édition dans `features/agents/`) : même filtre — seuls Claude/Codex proposés ; pas d'option custom. 3. Nettoie le code mort résultant (helpers `emptyCustomProfile`, validation custom) **uniquement** s'il n'est plus référencé ailleurs — sinon laisse-le et signale-le. 4. Vérifie `cd frontend && npm run build`. ## 3. Invariants - **Zéro régression** : la persistance/édition des profils déjà configurés n'est pas cassée ; un projet existant avec un agent Gemini/Aider/custom **legacy** continue de fonctionner (on restreint la **création**, pas l'exécution de l'existant). - Le prédicat `is_selectable` est la **source unique** ; back et front doivent rester cohérents. - Frontières : le front filtre/affiche selon le contrat du port, le back décide la sélectionnabilité. ## 4. Tests attendus (QA) **Rust** (`-p application`/`app-tauri`) : - `is_selectable` vrai pour Claude/Codex, faux pour Gemini/Aider. - la liste exposée à la sélection ne contient **que** Claude/Codex (custom absent). - non-régression : `reference_profiles()` (catalogue brut) contient toujours les 4 (data intacte). **Vitest** (`frontend`) : - le wizard first-run n'affiche que Claude/Codex ; **le bloc custom est absent** (`aria-label="add custom profile"` introuvable). - le sélecteur de création d'agent ne propose que Claude/Codex, pas de custom. - garde anti-always-green : un test qui vérifie l'**absence** du custom doit échouer si le bloc réapparaît (assertion sur non-présence d'un testid/label précis). ## 5. Méthode DevBackend → contrat + build vert → Main relaie à DevFrontend → build vert → QA écrit+exécute (`cargo test --workspace` ET `npx vitest run`) → vert. Rapport d'erreurs clair si rouge. **Ne pas committer, ne pas push.** ## 6. Références - Catalogue : `crates/application/src/agent/catalogue.rs` (`reference_profiles()` : claude+codex `with_structured_adapter`, gemini+aider sans) ; use cases : `…/agent/usecases.rs` (`ReferenceProfiles`). - Factory : `AgentSessionFactory::supports` (livré D2, `crates/infrastructure/src/session/factory.rs`). - DTO : `crates/app-tauri/src/dto.rs` (`ProfileDto`/`ProfileListDto`). - Front : `frontend/src/features/first-run/{FirstRunWizard,ProfilesSettings}.tsx`, `frontend/src/features/agents/`, `frontend/src/domain/index.ts` (`emptyCustomProfile`). - Spec : `ARCHITECTURE.md` §17.3, §17.6 et tableau §17.9 ligne **D7**.