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

@ -647,7 +647,7 @@ mod tests {
}
#[tokio::test]
async fn factory_passes_project_root_to_codex_add_dir() {
async fn factory_does_not_pass_project_root_to_codex_add_dir_without_policy() {
let factory = StructuredSessionFactory::new();
let (cmd, argv) = make_recording_fake(&[
r#"{"type":"thread.started","thread_id":"cx-new"}"#,
@ -679,8 +679,12 @@ mod tests {
let recorded = std::fs::read_to_string(&argv).expect("argv");
let args: Vec<&str> = recorded.lines().collect();
assert!(
args.windows(2).any(|w| w == ["--add-dir", "/project/root"]),
"factory must relay PreparedContext.project_root to Codex --add-dir, got: {args:?}"
!args.windows(2).any(|w| w == ["--add-dir", "/project/root"]),
"factory must not invent --add-dir project root without an explicit policy, got: {args:?}"
);
assert!(
!args.contains(&"--add-dir"),
"default Codex adapter launch must not carry any implicit --add-dir, got: {args:?}"
);
assert!(
!args.contains(&"--ask-for-approval"),