Dernier lot de §17 : seuls les profils pilotables en mode structuré sont proposés à la sélection/création. - domain : AgentProfile::is_selectable() = structured_adapter.is_some(), source unique de vérité du prédicat. - infrastructure : AgentSessionFactory::supports délègue à is_selectable (supports et is_selectable ne peuvent plus diverger). - application : selectable_reference_profiles() = reference_profiles() filtré ; ReferenceProfiles et FirstRunState exposent la liste filtrée (Claude/Codex). reference_profiles() brut reste à 4 (data intacte) ⇒ un agent Gemini/Aider/custom legacy déjà configuré continue de tourner. - frontend : bloc AddCustomProfile retiré du wizard first-run, action addCustom retirée du viewmodel ; wizard n'affiche que la liste filtrée. Tests (QA) : is_selectable (table de vérité), cohérence stricte is_selectable<->supports, liste exposée=2 / data brute=4, garde anti-régression Vitest sur l'absence du bloc custom — validées par mutation. cargo test --workspace : 820 passed. npx vitest run : 344 passed. §17 COMPLET (D0->D7). Suivi restant : D6b (surfacer reply dans le writer wire .response.json pour la délégation par protocole fichier). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
96 lines
5.7 KiB
Markdown
96 lines
5.7 KiB
Markdown
# 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**.
|