---
issueRef: "#99"
version: 3
updatedBy: {"kind":"agent","agent_id":"a6ced819-b893-4213-b003-9e9dc79b9641"}
updatedAt: 1785059758742
---
# Carnet #99 — Cadrage (prêt pour le cycle)

> Cadrage établi par Main après investigation code + **vérification empirique des binaires
> installés** (`codex-cli 0.145.0`, `Claude Code 2.1.220`). Les faits CLI ci-dessous sont
> **capitalisés et vérifiés** ; ils conditionnent toute l'architecture. Architect doit valider
> les ports/VO et les questions ouvertes (§6) avant l'implémentation.

---

## 1. État des lieux vérifié — comparaison des 3 moteurs

| Moteur | Modèle contrôlé par IdeA ? | Mécanisme | Source |
|---|---|---|---|
| **OpenCode** | ✅ Oui | `model` écrit à la racine du `opencode.json` isolé (`OPENCODE_CONFIG`), lu par la session structured | `lifecycle.rs:2686-2689`, `2786-2788` |
| **Codex** | ❌ Non | aucun `--model`, `codex_config_toml` n'écrit pas `model` → défaut du binaire | `codex.rs:215-239`, `lifecycle.rs:2941-2955` |
| **Claude** | ❌ Non | aucun `--model`, `claude_settings_seed` n'écrit pas `model` → défaut du binaire | `claude.rs:273-284`, `infrastructure/permission/claude.rs:59` |

PTY et headless sont **deux processus OS séparés** pour un même agent
(`allow_structured_alongside_pty: true`, `lifecycle.rs:1512-1517`) ; ils partagent le même
`CODEX_HOME`/cwd mais **aucun état modèle** n'est synchronisé entre eux côté IdeA. Le `/model`
de la TUI ne sort jamais du process TUI (pass-through brut, `terminal/usecases.rs:140`).

## 2. Faits CLI vérifiés (capitalisation) — la clé qui débloque tout

### Codex 0.145.0 — `model` est une clé config.toml **documentée**
- L'exemple littéral du `--help` est `-c model="o3"`. `config.toml` est chargé depuis
  `$CODEX_HOME/config.toml`.
- **Honorée par `codex` (TUI) ET `codex exec` (headless)** — même mécanisme de base
  (`--ignore-user-config` confirme qu'il est chargé par défaut).
- Bonus : `codex exec` accepte aussi `-m, --model <MODEL>` (seconde porte d'injection).
- **→ Écrire `model = "..."` dans le `$CODEX_HOME/config.toml` isolé fixe le défaut pour les
  DEUX canaux.**

### Claude 2.1.220 — `model` est gérable via settings + flag
- `--model <model>` fonctionne en interactif **et** `-p/--print` (headless).
- `--settings <file-or-json>` + la hiérarchie `./.claude/settings.local.json` (project-local)
  supportent une clé `model`.
- **→ Écrire `"model": "..."` dans le `.claude/settings.local.json` du run dir fixe le défaut
  pour les DEUX canaux** (cwd = run dir, lu par PTY et headless).

### Conclusion d'architecture
Les trois moteurs convergent vers **le même pattern** : écrire le modèle du profil dans le
**fichier de config isolé du run dir**. Le fichier partagé = point de vérité unique pour
headless + TUI au lancement. L'exigence produit « modèle par défaut = modèle headless = modèle
exposé TUI au lancement » est satisfaite **par construction**, sans sync à coder.

## 3. Points d'intégration IdeA précis (avec chemins)

| Moteur | Renderer à modifier | Fichier produit | Ownership | Isolation |
|---|---|---|---|---|
| **Codex** | `codex_config_toml` (`application/agent/lifecycle.rs:2941-2955`) | `$CODEX_HOME/config.toml` | `MergeToml` (préservé aux régénérations) | `CODEX_HOME={runDir}/.codex` déjà isolé (`lifecycle.rs:2361`) |
| **Claude** | `claude_settings_seed` (`infrastructure/src/permission/claude.rs:59`) | `{runDir}/.claude/settings.local.json` | `Replace` (régénéré du profil à chaque lancement) | cwd=run dir ; pas d'iso home mais **project-local override user** → le modèle IdeA gagne |

Champ existant à consommer ou déprécier : **`AgentProfile.model: Option<String>`**
(`domain/src/profile.rs:1008`) — actuellement code mort. Référence OpenCode à répliquer :
`OpenCodeProviderConfig { provider_id, model, api_key_ref }` (`domain/src/profile.rs:372-391`)
+ `SaveOpenCodeProviderProfile` (`application/agent/usecases.rs`).

## 4. Architecture proposée — découpage en lots

- **A. Domaine** — Nouveaux VO miroir d'`OpenCodeProviderConfig` :
  `CodexProviderConfig { provider, model, api_key_ref }` et `ClaudeProviderConfig { provider,
  model, api_key_ref }`. Rendre les backends mutuellement exclusifs par moteur (cf.
  `opencode_backend_is_consistent`, `domain/src/profile.rs:363-364,1224`). **Décision
  Architect** : nouveau VO vs réutiliser `AgentProfile.model` (voir §6).
- **B. Renderers de config** — `codex_config_toml` écrit `model` (+ `model_provider` si requis
  par la sémantique codex) ; `claude_settings_seed` écrit la clé `model`.
- **C. Use cases / persistance** — `SaveCodexProviderProfile` / `SaveClaudeProviderProfile`
  miroirs de `SaveOpenCodeProviderProfile`, même SecretRef pour la clé scellée.
- **D. UI / wizard** — duplication de profils Codex/Claude comme OpenCode (catalogue providers,
  sélection modèle, clé scellée). Réutiliser `provider_catalogue` + picker existants.
- **E. QA** — assert headless + TUI utilisent le modèle du profil (voir §5).

## 5. Invariants (à respecter, à tester)

- **Source de vérité unique** : le modèle vient du profil AI, écrit dans le fichier de config
  isolé du run dir, lu par headless **et** TUI au lancement.
- **Isolation préservée** : Codex via `CODEX_HOME` (rien ne change) ; Claude via project-local
  override (rien ne change). Pas d'accès au home global pour le modèle.
- **`/model` en cours de session TUI ne corrompt pas le headless** (acceptable : ne concerne que
  le process PTY en cours ; le prochain lancement réapplique le défaut du profil).
- **Non-régression OpenCode** : rendu `opencode.json` byte-identique (rien ne touche ce chemin).
- **Non-régression permissions** : `codex_config_toml` et `claude_settings_seed` continuent
  d'écrire `mcp`/`sandbox`/`trust`/`approval` comme aujourd'hui (le `model` s'ajoute).

## 6. Questions ouvertes pour Architect (à trancher avant DevBackend)

1. **VO** : créer `CodexProviderConfig`/`ClaudeProviderConfig` (homogène à OpenCode) **ou**
   consommer le champ existant `AgentProfile.model` (code mort) ? Recommandation Main : nouveaux
   VO pour rester isomorphe à OpenCode (provider + clé scellée), déprécier `AgentProfile.model`.
2. **Codex `model_provider`** : la config codex distingue `model` et `model_provider`. Faut-il
   exposer les deux au profil, ou `model` seul suffit (provider implicite) ?
3. **Claude** : clé `model` dans `settings.local.json` suffit, ou faut-il gérer aussi la clé
   API Anthropic du profil (SecretRef) pour que le modèle soit réellement joignable ?
4. **Catalogue providers** : quels providers exposer pour Codex (lié OpenAI) et Claude
   (Anthropic) ? Réutiliser `provider_catalogue.rs` ou catalogue dédié par moteur ?
5. **Sémantique TUI** : confirmer qu'aucune CLI n'écrase `config.toml`/`settings` au démarrage
   (risque équivalent au « gate refresh » du ticket #98 côté OpenCode).

## 7. Périmètre QA — 2 couches (cf. méthodologie #98)

- **Couche 1 (unitaire pure)** : le rendu `codex_config_toml` contient la clé `model`
  attendue ; `claude_settings_seed` contient `"model"` attendue ; non-régression
  byte-identique du reste (mcp/sandbox/trust/approval) ; exclusion mutuelle des backends.
- **Couche 2 (intégration gated)** : spawn réel `codex exec` et `claude -p` sur un profil avec
  modèle X → assert le modèle actif est X (pas le défaut binaire). Vérifier côté TUI aussi
  (modèle exposé au lancement). **Gate §6.5** à lever.

## 8. Hors périmètre (exclu de ce ticket)

- Override **runtime** du modèle à l'appel (`idea_ask_agent` porterait un modèle) — autre lot.
- Providers distants SSH/WSL (ne concerne que le spawn local).
- Détail de la `provider_catalogue` au-delà de la réutilisation (fast-follow).

---

## Contexte de mise à jour

- Cadrage Main après vérification empirique CLI (codex 0.145.0, claude 2.1.220).
- Lié à #98 (relatesTo) — même famille « modèle au spawn ».
- Prochaine étape cycle : **Architect** (valide ports/VO + tranche §6) → **Git** (branche) →
  **DevBackend** (lots A-C) + **DevFrontend** (lot D) → **QA** (2 couches) → **Git** (commit).
