feat(orchestrator): modèle de désignation d'orchestrateur + sink de diagnostic

Introduit le modèle AgentManifest { version, entries, orchestrator } et la
garde d'écriture directe may_write_directly(..., &OrchestratorDesignation) :
seul l'orchestrateur désigné peut écrire directement, les autres passent par
le rendez-vous médié. Câble la désignation à travers domain → application →
infrastructure → app-tauri (context_guard, service, lifecycle, ports).

Ajoute crates/application/src/diag.rs : sink de diagnostic best-effort, sans
dépendance, qui miroite les traces du rendez-vous inter-agents de
l'orchestrateur vers un fichier de log persistant (utile au lancement via
AppImage où stderr est jeté), avec la même discipline « zéro dépendance,
ne casse jamais le rendez-vous ».

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-20 08:56:17 +02:00
parent 40982d44da
commit 287681c198
57 changed files with 1758 additions and 420 deletions

View File

@ -122,6 +122,8 @@ pub struct CodexExecSession {
command: String,
/// Répertoire de travail (run dir isolé §14.1).
cwd: String,
/// Project/workspace roots that must be writable in Codex's CLI sandbox.
writable_roots: Vec<String>,
/// Id de conversation **du moteur** Codex, capté au premier tour, `None` avant.
conversation_id: Mutex<Option<String>>,
/// Plan de sandbox OS **par lancement** (lot LP4-4), porté dans chaque
@ -143,6 +145,7 @@ impl CodexExecSession {
command: impl Into<String>,
cwd: impl Into<String>,
seed_conversation_id: Option<String>,
writable_roots: Vec<String>,
sandbox: Option<SandboxPlan>,
sandbox_enforcer: Option<Arc<dyn SandboxEnforcer>>,
) -> Self {
@ -150,6 +153,7 @@ impl CodexExecSession {
id,
command: command.into(),
cwd: cwd.into(),
writable_roots,
conversation_id: Mutex::new(seed_conversation_id),
sandbox,
sandbox_enforcer,
@ -160,18 +164,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 <prompt>`.
/// --sandbox workspace-write --add-dir <project-root> <prompt>`.
/// - Reprise (id connu) : `codex exec resume <thread_id> --json
/// --skip-git-repo-check --sandbox workspace-write <prompt>`.
/// --skip-git-repo-check --sandbox workspace-write --add-dir <project-root>
/// <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`. Défaut
/// raisonnable, aligné sur l'autonomie projet (CLAUDE.md §12) ; à terme **piloté par
/// les permissions de l'agent** (`.ideai/permissions.json` + sandbox OS) — non
/// implémenté ici.
/// 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.
fn build_spawn_line(&self, prompt: &str) -> SpawnLine {
let mut args = vec!["exec".to_owned()];
if let Some(id) = self.conversation_id.lock().expect("mutex sain").as_ref() {
@ -182,6 +186,10 @@ impl CodexExecSession {
args.push("--skip-git-repo-check".to_owned());
args.push("--sandbox".to_owned());
args.push("workspace-write".to_owned());
for root in self.writable_roots.iter().filter(|root| !root.is_empty()) {
args.push("--add-dir".to_owned());
args.push(root.clone());
}
args.push(prompt.to_owned());
SpawnLine {
command: self.command.clone(),