# 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**.
