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())
.flatten()
.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();
out.sort();
out.dedup();
@@ -270,6 +277,23 @@ mod tests {
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
/// and a collect — a model asked to size a team proposes twelve happily.
#[test]