task #23: retire per-team ZeroClaw container coords (Option A)

Missions never populated teams.zeroclaw_container /
teams.zeroclaw_gateway_url — those were research/loops-era columns
for long-lived per-team containers. Every mission-materialized team
runs inside the SHARED runtime as claws-as-agents provisioned via
RuntimeProvisioner. Reading zeroclaw_container on a mission row
always came up NULL, making security_scan + benchmark_runner
silently fail with "mission has no team container yet."

Changes:
  - migrations/0054_drop_teams_zeroclaw_columns.sql — DROP both
    columns.
  - cm-db/src/repo/teams.rs — delete dead helpers
    team_container_coords + set_team_container_coords.
  - cm-api/src/security_scan.rs — replace team_container_for_mission
    with exec_target(pool, mission_id): container from env
    CLAWMATES_RUNTIME_CONTAINER (default clawmates-runtime); workdir
    from env CLAWMATES_MISSIONS_ROOT + /{mission_id}/repo
    (same convention pdf_renderer uses); precondition that mission
    must have repo_id bound.
  - cm-api/src/benchmark_runner.rs — same shape.

Follow-up (not in this commit): mission_orchestrator + compose stack
still need to wire a per-mission repo checkout under
CLAWMATES_MISSIONS_ROOT before scan/bench actually produce findings.
Columns cleanup here removes the misleading silent-fail; the
missing-checkout gap is now surfaced with a clear error.

Verified: SQLX_OFFLINE=true cargo check --workspace + cargo test
-p cm-api --test mission_orchestrator both green.

Closes task #23.
This commit is contained in:
Omar Sobh
2026-07-19 23:18:28 -07:00
parent 74ee990d7e
commit 854a617777
4 changed files with 108 additions and 98 deletions
-52
View File
@@ -318,55 +318,3 @@ pub async fn set_team_runtime_config(
Ok(())
}
/// 0046: read the team's per-container coordinates. Both fields NULL
/// means the team has never spawned; the runtime provisions on first
/// iteration.
pub async fn team_container_coords(
pool: &PgPool,
id: Uuid,
workspace_id: WorkspaceId,
) -> Result<Option<(Option<String>, Option<String>)>, DbError> {
use sqlx::Row;
let row: Option<sqlx::postgres::PgRow> = sqlx::query(
"SELECT zeroclaw_container, zeroclaw_gateway_url FROM teams
WHERE id = $1 AND workspace_id = $2",
)
.bind(id)
.bind(workspace_id.as_uuid())
.fetch_optional(pool)
.await?;
Ok(row.map(|r| {
(
r.try_get::<Option<String>, _>("zeroclaw_container")
.ok()
.flatten(),
r.try_get::<Option<String>, _>("zeroclaw_gateway_url")
.ok()
.flatten(),
)
}))
}
/// 0046: set (or clear) the team's per-container coordinates once
/// spawn_team lands them.
pub async fn set_team_container_coords(
pool: &PgPool,
id: Uuid,
workspace_id: WorkspaceId,
container: Option<&str>,
gateway_url: Option<&str>,
) -> Result<(), DbError> {
sqlx::query(
"UPDATE teams
SET zeroclaw_container = $3,
zeroclaw_gateway_url = $4
WHERE id = $1 AND workspace_id = $2",
)
.bind(id)
.bind(workspace_id.as_uuid())
.bind(container)
.bind(gateway_url)
.execute(pool)
.await?;
Ok(())
}