fix(missions): the third team gate, and a container a microvm mission never uses

on_launch demanded a team template too — "pick teams in the wizard" — so a
microvm mission still could not launch after the first two gates were exempted.
Three separate places required a claw graph for a path that runs one `claude -p`
inside a VM: routes::missions (draft→running), phase_runner::launch_phase (no
matching teams → stay pending), and here.

Returning before team materialisation rather than filtering its picks: claws that
never run are not a cheaper version of the same thing, they are a runtime binding
and a pairing code describing something nothing speaks to.

Also stops provisioning the per-mission ZeroClaw container for a microvm mission.
The first real run was observed starting one and leaving it holding a pairing code
and ~3 GB of image for the life of a mission that never contacts it.

461 tests pass, clippy clean.
This commit is contained in:
Omar Sobh
2026-08-05 16:47:08 -07:00
parent 0a9747091f
commit 521b8dea10
+24 -1
View File
@@ -79,7 +79,12 @@ pub async fn on_launch(
// The mission's own runtime endpoint. Claws MUST be provisioned against
// THIS gateway, not the global one — see RuntimeProvisioner::for_gateway.
let mut mission_gateway: Option<String> = None;
if let Some(prov) = crate::mission_runtime::MissionRuntimeProvisioner::from_env() {
// Not for a microVM mission: the ZeroClaw daemon it would start is never
// spoken to, and it would sit holding a pairing code and ~3 GB of image for
// the life of the mission. Observed doing exactly that on the first real run.
if let Some(prov) = crate::mission_runtime::MissionRuntimeProvisioner::from_env()
.filter(|_| mission.runtime_kind != "microvm")
{
match prov.ensure_container(mission_id).await {
Ok(ec) => {
mission_gateway = Some(ec.endpoint.clone());
@@ -115,6 +120,24 @@ pub async fn on_launch(
);
}
// A microVM mission materialises no team. Its phases run as one `claude -p`
// inside a VM (`microvm_executor`), so there is no claw graph to provision —
// and demanding one rejected the launch of a well-formed mission with "pick
// teams in the wizard". This is the third of three team gates on a path that
// uses no teams; the other two are in `routes::missions` (draft→running) and
// `phase_runner::launch_phase` (no matching teams → stay pending).
//
// Returning before the picks below, not filtering them, because provisioning
// claws that never run is not a cheaper version of the same thing — it is a
// runtime binding and a pairing code describing something nothing uses.
if mission.runtime_kind == "microvm" {
eprintln!(
"mission_orchestrator: mission {mission_id} is a microvm mission — no team to \
materialise; its phases execute in a VM"
);
return Ok(None);
}
// Skip team materialization if already bound.
if mission.team_id.is_some() {
eprintln!(