From 1678452a93aa9a306da23638c22b98b41d52139f Mon Sep 17 00:00:00 2001 From: Omar Sobh Date: Mon, 14 Sep 2026 10:23:21 -0500 Subject: [PATCH] fix(missions): delete takes the captured outputs with it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `mission_gc` keeps `_outputs/` for 90 days because they are artifacts a user can still open. After `DELETE /api/missions/{id}` nothing can: the `mission_artifacts` rows went with the mission. Wiping prod on 2026-09-14 found 163 such directories, the newest from a mission deleted twenty minutes earlier — every mission ever deleted had left its outputs to wait out a retention window that no longer meant anything. The delete path removes the directory now, after the container teardown and before the row goes. A failure logs and continues, and says the gc will get it in 90 days, which is what happened before on every delete. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WZb5A2kfVfjpdwSochkuHz --- crates/cm-api/src/routes/missions.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/crates/cm-api/src/routes/missions.rs b/crates/cm-api/src/routes/missions.rs index eda9d5f..3b10915 100644 --- a/crates/cm-api/src/routes/missions.rs +++ b/crates/cm-api/src/routes/missions.rs @@ -1068,6 +1068,24 @@ async fn reap_mission_resources(state: &AppState, mission_id: Uuid) { } } + // 6. The captured outputs. `mission_gc` keeps `_outputs/` for 90 days + // because they are artifacts a user can still open — but once the + // mission row is gone so are its `mission_artifacts`, and nothing can + // open them. Found on 2026-09-14 as 163 orphaned directories on prod, + // the newest belonging to a mission deleted twenty minutes earlier. + let outputs = crate::mission_workspace::missions_root() + .join("_outputs") + .join(mission_id.to_string()); + if outputs.is_dir() { + if let Err(e) = tokio::fs::remove_dir_all(&outputs).await { + eprintln!( + "missions::delete: remove {} failed (continuing; mission_gc will reap it in \ + 90 days): {e}", + outputs.display() + ); + } + } + // Say what actually happened. `failed > 0` means the mission row is about to // be deleted while its claws survive with nothing left pointing at them — // the orphan case, and the only way to notice it after the fact.