fix(missions): exclude build output from COLLECT too, not just inject

The other half of the same bug. The previous commit filtered `mission_fs::pack_dir`
(the inject side) and left the guest's `op_get` tarring everything, so the re-run
that proved the #54 fix — it survived 480s where it used to die at 210 — still lost
its work to `vm_collect ... node timed out`. Two modules written, four subagents
used, nothing delivered.

`op_get` now takes an `exclude` list, sent by the host from
`mission_fs::transport_excludes()` — the same list `mission_delivery` uses for the
diff. Policy in one place, applied at both ends of the wire. Matched on directory
NAME at any depth, so a workspace's per-crate `target/` dirs are all covered, with
a test that plants a nested one and asserts it does not come along.

Also proven by that run: the worker no longer kills a live microVM run. It ran 480
seconds straight through the 180s requeue window and the 210s mark where mission
019fd43e died, untouched. And `subagents: 4` — the team addendum did drive real
fan-out this time, which is the first evidence the Slice 3 switch does anything.

483 tests pass, clippy clean. Still to prove: a >3-minute mission that actually
DELIVERS. The collect fix is tested in isolation but has not yet carried a real
mission's work back, and the guest agent needs rebuilding into the rootfs before it
can.
This commit is contained in:
Omar Sobh
2026-08-06 09:18:53 -07:00
parent 4efcde9d4f
commit d49acaed5e
4 changed files with 131 additions and 8 deletions
+9 -4
View File
@@ -427,9 +427,14 @@ pub async fn exec(
}
/// Tar a path out of the guest.
pub async fn collect(vms: &Vms, vm_id: &str, path: &str) -> Result<Value, String> {
pub async fn collect(
vms: &Vms,
vm_id: &str,
path: &str,
exclude: Option<&Value>,
) -> Result<Value, String> {
let uds = uds_of(vms, vm_id).await?;
rpc(&uds, &json!({ "op": "get", "path": path })).await
rpc(&uds, &json!({ "op": "get", "path": path, "exclude": exclude })).await
}
/// SIGKILL a whole process group, ignoring "already gone".
@@ -565,7 +570,7 @@ pub async fn handle_op(op: &str, v: &Value, vms: &Vms) -> (bool, String) {
)
.await
}
"vm_collect" => collect(vms, &vm_id, &s("path")).await,
"vm_collect" => collect(vms, &vm_id, &s("path"), v.get("exclude")).await,
"vm_destroy" => destroy(vms, &vm_id).await,
"vm_list" => Ok(list(vms).await),
other => Err(format!("unknown vm op: {other}")),
@@ -801,7 +806,7 @@ pub async fn selftest() -> bool {
// Work produced in the guest must come back out.
let _ = exec(&vms, id, "echo PRODUCED-OK > /work/out.txt", None, 30, None).await;
let r = collect(&vms, id, "/work").await;
let r = collect(&vms, id, "/work", None).await;
let round_tripped = r
.as_ref()
.ok()