missions: fix repo checkout for retries + tokenize git.redclaw.dev clones
ci / gates (push) Successful in 7s
ci / frontend (push) Successful in 37s
ci / rust (push) Successful in 3m40s
ci / e2e (push) Skipped
ci / publish (push) Successful in 2m37s

- mission_orchestrator: run ensure_checkout BEFORE the team_id
  short-circuit. Previously, a re-launched or retried mission bailed
  out at the team_id=already-bound guard and skipped repo checkout
  entirely, so agents ran against an empty workspace.
- mission_workspace: inject GITEA_TOKEN into git.redclaw.dev URLs so
  clone auth works from the server container. Redact any token
  echoed back on failure.
- refresh buttons on MissionCanvas + MissionsList now spin the icon
  while loading so clicks are visibly acknowledged.
- refresh-spinner keyframe added to motion.css.

Requires operator on gw-04: sudo chown 65532:65532 /var/lib/clawmates-missions
(applied 2026-07-21 pre-commit).
This commit is contained in:
Omar Sobh
2026-07-21 20:33:33 -07:00
parent f1f3de4db0
commit 1e91a19707
5 changed files with 87 additions and 30 deletions
+23 -19
View File
@@ -39,6 +39,7 @@ pub async fn on_launch(
mission_id: Uuid,
node_hub: Option<std::sync::Arc<crate::fleet::NodeHub>>,
) -> Result<Option<Uuid>, String> {
eprintln!("mission_orchestrator::on_launch fired mission_id={mission_id}");
let Some(mission) = cm_db::repo::missions::get(pool, mission_id, workspace_id.as_uuid())
.await
.map_err(|e| format!("load mission: {e}"))?
@@ -46,8 +47,29 @@ pub async fn on_launch(
return Err("mission not found".into());
};
// Skip if already bound.
// ensure_checkout is idempotent (fetch+reset on existing clones,
// clone on missing dirs) so we run it BEFORE the team_id short-
// circuit: a re-launched or retried mission still needs a fresh
// repo checkout even though its team was minted on the first
// launch. Non-fatal — logs and continues on failure.
match crate::mission_workspace::ensure_checkout(pool, workspace_id, mission_id).await {
Ok(Some(path)) => eprintln!(
"mission_orchestrator: repo checked out at {} for mission {mission_id}",
path.display()
),
Ok(None) => eprintln!(
"mission_orchestrator: mission {mission_id} has no repo bound, skipping checkout"
),
Err(e) => eprintln!(
"mission_orchestrator: repo checkout for {mission_id} failed (continuing): {e}"
),
}
// Skip team materialization if already bound.
if mission.team_id.is_some() {
eprintln!(
"mission_orchestrator::on_launch team_id already bound for mission_id={mission_id} — skipping team materialization"
);
return Ok(mission.team_id);
}
@@ -139,24 +161,6 @@ pub async fn on_launch(
.await
.map_err(|e| format!("bind team on mission: {e}"))?;
// Ensure the mission's repo is checked out at
// $CLAWMATES_MISSIONS_ROOT/{mission_id}/repo — where
// security_scan + benchmark_runner exec against. Non-fatal:
// missions without a repo (research_only, custom) skip cleanly,
// and clone failures log without blocking launch (the operator
// sees the error on the canvas via the run's failed status when
// a repo-dependent phase tries to fire).
match crate::mission_workspace::ensure_checkout(pool, workspace_id, mission_id).await {
Ok(Some(path)) => eprintln!(
"mission_orchestrator: repo checked out at {}",
path.display()
),
Ok(None) => {}
Err(e) => eprintln!(
"mission_orchestrator: repo checkout for {mission_id} failed (continuing): {e}"
),
}
// Herdr second-runtime: if runtime_kind='local_herdr', spawn a
// pane on target_node running the first available local CLI.
// Non-fatal on failure — the operator sees the error in server