fix(phase_runner): re-mint pairing code on every launch
ci / gates (push) Successful in 7s
ci / frontend (push) Successful in 40s
ci / rust (push) Successful in 3m36s
ci / e2e (push) Skipped
ci / publish (push) Successful in 2m33s

Pairing codes are single-use / expiring — a mission that reuses an
existing runtime container on a retry needs a fresh code, not the
stale one from the initial launch. Drop the runtime_endpoint gate
so ensure_container always fires, and its fast path re-mints via
/admin/paircode/new for existing containers.
This commit is contained in:
Omar Sobh
2026-07-23 09:29:49 -07:00
parent 8ba9bf0c1c
commit aea732e712
+30 -29
View File
@@ -167,37 +167,38 @@ async fn launch_phase(
// Provision the per-mission runtime container if not bound. // Provision the per-mission runtime container if not bound.
// Idempotent — on_launch sets this on initial launch, but pre-C3 // Idempotent — on_launch sets this on initial launch, but pre-C3
// missions or retries against a torn-down container land here. // missions or retries against a torn-down container land here.
let mission = cm_db::repo::missions::get(pool, mission_id, workspace_id) // Always call ensure_container — the fast path re-mints a fresh
.await // one-time pairing code even for existing containers. Old codes
.map_err(|e| format!("load mission for runtime binding: {e}"))?; // expire / are single-use, so a launch that reuses a container
if let Some(m) = mission { // still needs a fresh code for the topology_worker's next /pair.
if m.runtime_endpoint.is_none() { if let Some(prov) = crate::mission_runtime::MissionRuntimeProvisioner::from_env() {
if let Some(prov) = crate::mission_runtime::MissionRuntimeProvisioner::from_env() { match prov.ensure_container(mission_id).await {
match prov.ensure_container(mission_id).await { Ok(ec) => {
Ok(ec) => { let name = crate::mission_runtime::container_name(mission_id);
let name = crate::mission_runtime::container_name(mission_id); if let Err(e) = cm_db::repo::missions::set_runtime_binding(
if let Err(e) = cm_db::repo::missions::set_runtime_binding( pool,
pool, mission_id,
mission_id, workspace_id,
workspace_id, Some(&name),
Some(&name), Some(&ec.endpoint),
Some(&ec.endpoint), ec.pairing_code.as_deref(),
ec.pairing_code.as_deref(), )
) .await
.await {
{ eprintln!(
eprintln!("phase_runner: bind runtime container for {mission_id} failed: {e}"); "phase_runner: bind runtime container for {mission_id} failed: {e}"
} else { );
eprintln!( } else {
"phase_runner: runtime container {name} → {} (paired={}) for mission {mission_id}", eprintln!(
ec.endpoint, "phase_runner: runtime container {name} → {} (paired={}) for mission {mission_id}",
ec.pairing_code.is_some() ec.endpoint,
); ec.pairing_code.is_some()
} );
}
Err(e) => eprintln!("phase_runner: provision runtime container for {mission_id} failed (continuing): {e}"),
} }
} }
Err(e) => eprintln!(
"phase_runner: provision runtime container for {mission_id} failed (continuing): {e}"
),
} }
} }