dashboard: GC orphan approvals on render (agent state dir missing)

This commit is contained in:
müde 2026-05-15 01:21:31 +02:00
parent 99867195e5
commit a146b147ff

View file

@ -58,7 +58,10 @@ async fn index(headers: HeaderMap, State(state): State<AppState>) -> Html<String
let hostname = host.split(':').next().unwrap_or(host).to_owned(); let hostname = host.split(':').next().unwrap_or(host).to_owned();
let containers = lifecycle::list().await.unwrap_or_default(); let containers = lifecycle::list().await.unwrap_or_default();
let approvals = state.coord.approvals.pending().unwrap_or_default(); let approvals = gc_orphans(
&state.coord,
state.coord.approvals.pending().unwrap_or_default(),
);
let approvals_html = render_approvals(&approvals).await; let approvals_html = render_approvals(&approvals).await;
Html(format!( Html(format!(
@ -156,6 +159,26 @@ async fn render_approvals(approvals: &[Approval]) -> String {
out out
} }
/// Filter out approvals whose agent state dir was wiped out from under us
/// (e.g. by a test script's cleanup). Marks them failed so they fall out of
/// `pending` on next render.
fn gc_orphans(coord: &Coordinator, approvals: Vec<Approval>) -> Vec<Approval> {
approvals
.into_iter()
.filter(|a| {
if Coordinator::agent_proposed_dir(&a.agent).exists() {
true
} else {
let _ = coord
.approvals
.mark_failed(a.id, "agent state dir missing");
tracing::info!(id = a.id, agent = %a.agent, "auto-failed orphan approval");
false
}
})
.collect()
}
async fn approval_diff(agent: &str, commit_ref: &str) -> String { async fn approval_diff(agent: &str, commit_ref: &str) -> String {
let proposed = Coordinator::agent_proposed_dir(agent); let proposed = Coordinator::agent_proposed_dir(agent);
if !proposed.exists() { if !proposed.exists() {