fix(missions): a bootable rootfs is not a runnable one

Found by looking at what the fleet actually reports, not by reasoning about it:
tank's `capabilities.rootfs` is `["agent-terminal", "claude", "default"]`. Slice 5
offered that list to the planner as the menu of backends and validated proposals
against it — so a roster naming `agent-terminal` would have been proposed,
validated, approved and launched, and then failed at the agent turn, because
`microvm_credential_for` has no contract for it and refuses rather than forward
an Anthropic subscription token to an unknown endpoint.

Refusing at boot is correct and is exactly the wrong PLACE: it is three steps and
one human approval after the point where the answer was already knowable. The
menu is now the intersection of "a node can boot it" and "a mission agent can
authenticate in it", which is what the question meant all along.

`backend_can_run_a_mission` derives from the credential contract rather than
restating it, so a backend gaining one (GLM and Kimi, when B4.6's base-URL
contract is settled) becomes proposable in the same commit that makes it
runnable — instead of in a second list someone has to remember.

528 tests pass, clippy clean.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Omar Sobh
2026-08-06 16:27:25 -07:00
co-authored by Claude Opus 5
parent 1797669296
commit aa470091aa
2 changed files with 35 additions and 0 deletions
+24
View File
@@ -203,6 +203,13 @@ pub async fn available_backends(
.filter_map(|(v,)| v.as_array().cloned()) .filter_map(|(v,)| v.as_array().cloned())
.flatten() .flatten()
.filter_map(|v| v.as_str().map(str::to_string)) .filter_map(|v| v.as_str().map(str::to_string))
// A node reports every rootfs it has BUILT, which is not the same as
// every rootfs a mission can run in. `agent-terminal` is on tank right
// now: bootable, and with no credential contract, so an agent inside it
// has nothing to authenticate with. Offering it to the planner would
// produce a roster that validates, approves, launches, and then fails at
// the agent turn — the expensive kind of late.
.filter(|b| crate::mission_runtime::backend_can_run_a_mission(b))
.collect(); .collect();
out.sort(); out.sort();
out.dedup(); out.dedup();
@@ -270,6 +277,23 @@ mod tests {
assert!(r.validate(&["claude".to_string()]).is_ok()); assert!(r.validate(&["claude".to_string()]).is_ok());
} }
/// A bootable image is not necessarily a runnable one. tank reports
/// `agent-terminal` in its rootfs list today: a real image, with no
/// credential contract, so an agent booted into it has nothing to
/// authenticate with. Offering it to the planner would produce a roster that
/// validates, approves, launches and then fails at the agent turn.
#[test]
fn only_backends_that_can_authenticate_are_offered() {
assert!(crate::mission_runtime::backend_can_run_a_mission("claude"));
assert!(crate::mission_runtime::backend_can_run_a_mission("default"));
for unrunnable in ["agent-terminal", "agent-browser", "rootfs-opus"] {
assert!(
!crate::mission_runtime::backend_can_run_a_mission(unrunnable),
"{unrunnable} has no credential contract and must not be proposable"
);
}
}
/// The ceiling. Each member is a VM boot, an inject, a full agent session /// The ceiling. Each member is a VM boot, an inject, a full agent session
/// and a collect — a model asked to size a team proposes twelve happily. /// and a collect — a model asked to size a team proposes twelve happily.
#[test] #[test]
+11
View File
@@ -146,6 +146,17 @@ pub fn microvm_provider_env(backend: Option<&str>) -> Result<Vec<(String, String
/// at is worse than not launching. GLM and Kimi reach their own endpoints via /// at is worse than not launching. GLM and Kimi reach their own endpoints via
/// `ANTHROPIC_BASE_URL` and need that contract settled (B4.6) before a VM can /// `ANTHROPIC_BASE_URL` and need that contract settled (B4.6) before a VM can
/// carry their keys — guessing it here would send an Anthropic token to z.ai. /// carry their keys — guessing it here would send an Anthropic token to z.ai.
/// Can a mission agent authenticate in a VM booted from this image?
///
/// The honest answer to "which backends can run a mission", which is NOT the
/// same as "which rootfs images exist on a node". A fleet node reports every
/// image it has built — `agent-terminal` among them — and booting a mission into
/// one with no credential contract fails at the agent turn, after the mission
/// was created and its roster approved.
pub fn backend_can_run_a_mission(backend: &str) -> bool {
microvm_credential_for(Some(backend)).is_ok()
}
fn microvm_credential_for(backend: Option<&str>) -> Result<&'static str, String> { fn microvm_credential_for(backend: Option<&str>) -> Result<&'static str, String> {
match backend { match backend {
None | Some("") | Some("default") | Some("claude") => Ok("CLAUDE_CODE_OAUTH_TOKEN"), None | Some("") | Some("default") | Some("claude") => Ok("CLAUDE_CODE_OAUTH_TOKEN"),