fix(sandbox): accès Git minimal pour Codex — writable roots restreints au .git résolu, sans blanket grant project (#195, QA verte)

Codex ne pouvait pas accéder aux métadonnées Git sous sandbox workspace-write.
À l'inverse, la factory relayait jusqu'ici le project_root entier en --add-dir,
soit un grant large non défendable.

Change pour n'accorder en écriture (Codex --add-dir ET SandboxPlan OS) que le
.git réellement résolu :
- resolve_git_dir gère .git/ directory ET fichier worktree 'gitdir: <path>'
  (absolu ou relatif au parent du marker), puis canonicalize.
- enrich_sandbox_plan_with_codex_git_roots ajoute/déduplique les roots en RW
  sur le SandboxPlan existant (préserve l'autorité EffectivePermissions).
- factory: défaut writable_roots = Vec::new() en l'absence de policy explicite.

Invariants conservés :
- project_root n'est jamais injecté comme writable root.
- aucun élargissement au parent commun (canonicalize + BTreeSet).
- permissions MCP/globales inchangées (seul le plan runtime est enrichi).

Tests (crates/application) :
- standard .git/ dir -> root unique.
- projet non-Git -> aucun root.
- worktree (.git file gitdir:) -> résolution + canonicalization.
- symlink -> dédup, pas de grant parent.
- factory: défaut sans policy n'émet plus --add-dir project_root.

Réserve QA: launch_conventionfile_injects_assigned_skills_in_order
(agent_lifecycle) arbitré hors-scope/non bloquant pour #195.
This commit is contained in:
2026-08-12 16:41:25 +02:00
parent 8a66ccbc07
commit e2fc85b434
5 changed files with 295 additions and 21 deletions

View File

@ -451,18 +451,18 @@ impl CodexExecSession {
///
/// Format RÉEL vérifié 2026-06-10 (codex 0.137.0) :
/// - Conversation neuve : `codex exec --json --skip-git-repo-check
/// --sandbox workspace-write --add-dir <project-root> <prompt>`.
/// --sandbox workspace-write [--add-dir <explicit-root>...] <prompt>`.
/// - Reprise (id connu) : `codex exec --json
/// --skip-git-repo-check --sandbox workspace-write --add-dir <project-root> resume
/// <thread_id> <prompt>`.
/// --skip-git-repo-check --sandbox workspace-write [--add-dir <explicit-root>...]
/// resume <thread_id> <prompt>`.
///
/// **Autonomie d'écriture (D3)** : `--sandbox workspace-write` autorise l'agent à
/// écrire dans son workspace. `codex exec` est déjà non-interactif (aucun prompt
/// d'approbation possible), donc on ne passe **pas** `--ask-for-approval` : ce flag
/// appartient à la commande interactive `codex`, pas à la sous-commande `exec` qui
/// sort sur `error: unexpected argument '--ask-for-approval' found`. Comme l'agent
/// tourne depuis son run dir isolé, `--add-dir` expose explicitement le project root
/// à la sandbox Codex pour que les écritures Git touchent le vrai workspace.
/// tourne depuis son run dir isolé, `--add-dir` expose uniquement les roots
/// writable explicitement fournis par la launch policy.
fn build_spawn_line(&self, prompt: &str) -> SpawnLine {
let mut args = vec!["exec".to_owned()];
let conversation_id = self.conversation_id.lock().expect("mutex sain").clone();