missions: topology_worker dials per-mission runtime endpoint (C3 slice 3)
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 28s
ci / rust (push) Failing after 1m23s
ci / e2e (push) Skipped
ci / publish (push) Skipped

When a topology_run is bound to a mission whose runtime_endpoint is
set, the worker constructs ZeroClawDriveExecutor against that URL
instead of the env-derived shared gateway. Falls back to shared for
non-mission runs and pre-C3 missions.

With slices 1-3 combined, a mission launched after this deploy will:
  1. get its per-mission container spawned during on_launch
  2. have its checkout dropped into /var/lib/clawmates-missions/<id>
     which is bind-mounted to /mission inside that container
  3. run its agents against ZEROCLAW_WORKSPACE=/mission/repo — so
     they can see and edit only this missions repo, no bleed-over.
This commit is contained in:
Omar Sobh
2026-07-21 22:31:42 -07:00
parent 7649b213ad
commit 82966a8004
2 changed files with 23 additions and 12 deletions
+1 -5
View File
@@ -147,11 +147,7 @@ async fn launch_phase(
// pre-C3 missions or retries against a torn-down container land // pre-C3 missions or retries against a torn-down container land
// here. Non-fatal: if docker is unreachable the run falls back to // here. Non-fatal: if docker is unreachable the run falls back to
// the shared runtime. // the shared runtime.
let mission = cm_db::repo::missions::get( let mission = cm_db::repo::missions::get(pool, mission_id, workspace_id)
pool,
mission_id,
workspace_id,
)
.await .await
.map_err(|e| format!("load mission for runtime binding: {e}"))?; .map_err(|e| format!("load mission for runtime binding: {e}"))?;
if let Some(m) = mission { if let Some(m) = mission {
+20 -5
View File
@@ -148,11 +148,26 @@ async fn run_job(
.and_then(|c| serde_json::from_value(c).ok()) .and_then(|c| serde_json::from_value(c).ok())
.unwrap_or_default(); .unwrap_or_default();
// Missions-era runs drive through the shared, env-derived ZeroClaw // C3: prefer the mission's per-run runtime endpoint when set on
// gateway — per-claw provisioning happens ahead of time via // the missions row; else fall back to the shared env-derived
// `RuntimeProvisioner` (see `mission_orchestrator::on_launch`), so // gateway (pre-C3 missions + non-mission runs). This is what
// there's no per-run container/gateway resolution left to do here. // isolates agents' workspace filesystem to that mission's repo.
let leaf_result = ZeroClawDriveExecutor::from_env(); let mission_endpoint: Option<String> = sqlx::query_scalar::<_, Option<String>>(
"SELECT m.runtime_endpoint
FROM topology_runs r
JOIN missions m ON m.id = r.mission_id
WHERE r.id = $1",
)
.bind(id)
.fetch_optional(pool)
.await
.ok()
.flatten()
.flatten();
let leaf_result = match mission_endpoint {
Some(url) => ZeroClawDriveExecutor::from_env_for_gateway(url),
None => ZeroClawDriveExecutor::from_env(),
};
let leaf = match leaf_result { let leaf = match leaf_result {
Ok(e) => e, Ok(e) => e,
Err(e) => { Err(e) => {