fix(backend): exclusion mutuelle des backends OpenCode (llamacpp vs cloud) (#97)

Les builders `with_opencode` / `with_opencode_provider` s'évacuent
réciproquement (dernier appel gagnant) pour honorer l'invariant
`opencode_backend_is_consistent`. Le use case SaveOpenCodeProviderProfile
route désormais via le builder au lieu de muter le champ directement — c'est
ce qui dupliquait `opencode` + `opencodeProvider` et forçait un repli sur
llamacpp même quand l'utilisateur choisissait un provider cloud.

Défense en profondeur côté store :
- `read_doc` répare les profils corrompus sur disque (drop du stale `opencode`)
- `save` refuse de persister un profil violant l'invariant via le nouveau
  variant `StoreError::Invalid` (mappé vers `AppError::Invalid`)

Tests verts : builders last-wins (domain), save rejets + read repair
(infra, profile_store 10/10).

Refs #97
This commit is contained in:
2026-07-24 19:52:34 +02:00
parent 7fee56acf5
commit 0f0a76d806
6 changed files with 173 additions and 10 deletions

View File

@ -134,6 +134,7 @@ impl From<StoreError> for AppError {
fn from(e: StoreError) -> Self {
match e {
StoreError::NotFound => Self::NotFound("store item".to_owned()),
StoreError::Invalid(message) => Self::Invalid(message),
other => Self::Store(other.to_string()),
}
}