---
issueRef: "#97"
version: 3
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1784915626417
---
## Décision Architect (cadrage validé)

Bug 100% backend, deux défauts composés :
1. `SaveOpenCodeProviderProfile::execute` (`usecases.rs:364-367`) pose `opencode_provider` sans `opencode = None`.
2. Invariant `opencode_backend_is_consistent` (`profile.rs:1220`) existe mais jamais appelé (garde morte).
Lecture priorise `opencode` → cloud écrasé (`lifecycle.rs:2367`, `assistant/mod.rs:252`).

### Périmètre du lot (un seul, parallélisable)
- **DevBackend** :
  - Domaine : `with_opencode`/`with_opencode_provider` (`profile.rs:1132,1140`) imposent l'exclusion mutuelle (chacun efface l'autre).
  - Infrastructure : `FsProfileStore::save` rejette tout profil incohérent → `AppError::Invalid` (active enfin le prédicat).
  - Application : `SaveOpenCodeProviderProfile` reconstruit via builder (`with_opencode_provider`) ; idem pour `SaveProfile`/`ConfigureProfiles`.
  - **Migration requise** : à la lecture (ou passe dédiée), quand `opencode` ET `opencodeProvider` présents → dropper `opencode` stale (l'intention est cloud). Sinon ne corrige que les nouveaux profils.
- **DevFrontend** : strip de la config inactive au save selon le mode courant (requis pour SaveProfile/ConfigureProfiles qui ne portent pas d'intention explicite ; backend reste autorité via la garde).
- **QA** : tests unitaires domaine (builders exclusifs) + garde store + intégration création profil cloud + scénario migration profils corrompus.

### Décisions de frontière
- DTO `SaveOpenCodeProviderProfileRequestDto` **inchangé** (whole-profile) ; output = profil normalisé, autorité pour le frontend.
- Priorité lecture `opencode` d'abord **gardée** (irrelevant post-exclusion), commenter comme fallback défensif.
- Duplication de la résolution (lifecycle.rs:2367 + assistant/mod.rs:252) = dette hexagonale préexistante, **hors périmètre de ce lot** (suivre, ne pas refactoriser ici).

Voir mémoire projet `ticket97-opencode-provider-mutual-exclusion`.