From 6665bd913d33b22f722159e8bbd604b508d4e29f Mon Sep 17 00:00:00 2001 From: Doron Chen Date: Mon, 10 Aug 2026 10:18:25 +0300 Subject: [PATCH] fix(compose): bind output volume to host path and pre-create it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit services.yaml's `output` volume previously used an anonymous managed volume, leaving results with no host-visible path. Bind it to ./output/{benchmark}/{task-id}/ via driver_opts (compose/RULES.md rule 18) using ${PWD}, which resolves to the CLI's invocation directory regardless of include: nesting. That bind form doesn't auto-create the target directory the way a short-syntax host bind would, so the CLI now pre-creates it as the invoking user before running compose — otherwise Docker creates it root-owned on first mount, which the agent's uid-1002 process can't write into. Signed-off-by: Doron Chen --- cli/src/run.rs | 14 ++++++++++++++ containers/compose/services.yaml | 9 +++++++++ 2 files changed, 23 insertions(+) diff --git a/cli/src/run.rs b/cli/src/run.rs index 67a0be8e..df36c90f 100644 --- a/cli/src/run.rs +++ b/cli/src/run.rs @@ -253,6 +253,20 @@ fn run_compose( return Ok(()); } + // `services.yaml`'s `output` volume binds to `./output/{benchmark}/{task}` + // (compose/RULES.md rule 18) via a `driver_opts.device:` path — unlike a + // short-syntax host bind, that form does not auto-create the directory, + // so pre-create it here (as the invoking user, so the agent's uid-1002 + // process can still write into it — Docker would otherwise make it + // root-owned on first mount). + let task_id = envs + .iter() + .find(|(k, _)| *k == "EVAL_TASK_ID") + .map(|(_, v)| v.as_str()) + .unwrap_or("0"); + std::fs::create_dir_all(format!("output/{benchmark}/{task_id}")) + .map_err(|e| format!("failed to create host output dir: {e}"))?; + let mut cmd = Command::new("docker"); cmd.arg("compose").arg("-f").arg(&compose_ref); // `-y`: a published `oci://` stack prompts to confirm (and echoes) the diff --git a/containers/compose/services.yaml b/containers/compose/services.yaml index 4d504b54..db9c2990 100644 --- a/containers/compose/services.yaml +++ b/containers/compose/services.yaml @@ -55,4 +55,13 @@ networks: upstream: {} volumes: + # Bind to the host so results land at ./output/{benchmark}/{task-id}/ + # (compose/RULES.md rule 18) instead of an anonymous managed volume with no + # host-visible path. `${PWD}` resolves to the CLI's invocation directory, + # not this file's directory, regardless of `include:` nesting. output: + driver: local + driver_opts: + type: none + o: bind + device: ${PWD}/output/${EVAL_BENCHMARK:-aime}/${EVAL_TASK_ID:-0}