3.3 KiB
3.3 KiB
name, description, metadata
| name | description | metadata | ||
|---|---|---|---|---|
| ticket97-opencode-provider-mutual-exclusion | memory note ticket97-opencode-provider-mutual-exclusion |
|
Ticket #97 — Exclusion mutuelle opencode / opencodeProvider (décision Architect)
Bug
Wizard first-run : profiles.json contient à la fois opencode (llamacpp) et opencodeProvider (cloud). Lecture priorise opencode → llamacpp l'emporte silencieusement.
Root cause (vérifiée dans le code)
SaveOpenCodeProviderProfile::execute(crates/application/src/agent/usecases.rs:364-367) poseopencode_providersans faireopencode = None.- Invariant
opencode_backend_is_consistent(crates/domain/src/profile.rs:1220) existe + testé mais jamais appelé hors tests (garde morte). - Lecture priorise
opencode:crates/application/src/agent/lifecycle.rs:2367+crates/infrastructure/src/assistant/mod.rs:252.
Décisions Architect (validées)
- Lot : un seul lot cohérent. Backend = autoritaire (invariant + garde + use case via builder + migration). Frontend = strip de la config inactive au save selon le mode (requis pour les chemins SaveProfile/ConfigureProfiles qui ne portent pas d'intention explicite). Découplés/parallélisables. DevBackend + DevFrontend + QA.
- Frontière :
- Domaine : builders
with_opencode(profile.rs:1132) etwith_opencode_provider(profile.rs:1140) doivent imposer l'exclusion mutuelle (chacun efface l'autre). Aujourd'hui ce sont des setters muets = la faille. - Infrastructure :
FsProfileStore::saverejette tout profil violant l'invariant →AppError::Invalid(c'est ici que le prédicat enfin s'appelle). Défense en profondeur. - Application : use cases utilisent les builders, jamais la mutation brute de champ.
- Refuser l'ad-hoc
opencode = Nonepar use case (DRY, 4 chemins d'écriture).
- Domaine : builders
- DTO : garder
SaveOpenCodeProviderProfileRequestDto(dto.rs:1148) whole-profile (zéro cassure frontend). Leopencodeparasite devient inoffensif car le use case reconstruit via builder. Output = profil normalisé, autorité pour le frontend. Corriger aussi SaveProfile (usecases.rs:269) et ConfigureProfiles (usecases.rs:460). - Lecture priorité : garder
opencoded'abord (irrelevant post-exclusion), commenter comme fallback défensif. - Duplication résolution (lifecycle.rs:2367 + assistant/mod.rs:252) : dette hexagonale préexistante, NE PAS élargir ce lot. Suivre via un futur
AgentProfile::effective_opencode_backend()ou générateur partagé. - Migration REQUISE : profils déjà corrompus sur disque portent les deux. Recovery : à la lecture (ou passe de migration), quand les deux présents, dropper
opencodestale (l'intention est cloud, le bug ne venant que d'une action cloud explicite). Sans cela, #97 ne corrige que les nouveaux profils.
Sites de résolution (priorité) = exactement 2
crates/application/src/agent/lifecycle.rs:2367crates/infrastructure/src/assistant/mod.rs:252Autres accès.opencodescoper en mode local, non affectés :lifecycle.rs:2455-2468,model_server.rs:121-127,catalogue.rs:244-258,usecases.rs:410.
Statut
Décision Architect posée. À faire implémenter par DevBackend (domaine builders + garde store + migration + use cases) et DevFrontend (strip au save). QA : tests domaine/store + intégration cloud + scénario migration profils corrompus.