fix(missions): delete takes the captured outputs with it
deploy / test (push) Successful in 4m55s
deploy / build (push) Successful in 5m35s

`mission_gc` keeps `_outputs/<id>` 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 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WZb5A2kfVfjpdwSochkuHz
This commit is contained in:
Omar Sobh
2026-09-14 10:23:21 -05:00
co-authored by Claude Opus 5
parent 93a386e706
commit 1678452a93
+18
View File
@@ -1068,6 +1068,24 @@ async fn reap_mission_resources(state: &AppState, mission_id: Uuid) {
}
}
// 6. The captured outputs. `mission_gc` keeps `_outputs/<id>` 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.