From c264a3567cad1bb38e93eb6d12a989bb0d8831b6 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Sat, 19 Sep 2026 18:56:48 -0400 Subject: [PATCH] Settle the managed run as soon as its terminal record is stored The in-process Petri path persisted the run's terminal lifecycle record, settled the projector, aggregated usage, and only then settled the managed run. GET /runs/{id} reads the stored summary, so it reported the run as ended while the delete precheck, which prefers the managed run, still saw it running and refused the delete as active. The prune scenario hit that window about once in thirty runs. Settle the managed run right after the record is stored, before the view catches up. Co-Authored-By: Claude Fable 5.1 --- lib/apps/fabro-server/src/server/petri_runs.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/apps/fabro-server/src/server/petri_runs.rs b/lib/apps/fabro-server/src/server/petri_runs.rs index 2963cdcd7..91e54c5f7 100644 --- a/lib/apps/fabro-server/src/server/petri_runs.rs +++ b/lib/apps/fabro-server/src/server/petri_runs.rs @@ -498,6 +498,11 @@ pub(crate) async fn execute(state: Arc, run_id: RunId) { if let Err(err) = run_records::lifecycle(&state, run_id, record).await { error!(run_id = %run_id, error = %err, "Failed to persist run outcome"); } + // The run reads as ended from the moment its terminal record is stored, + // so the managed run settles here, before the view catches up: a delete + // that arrives between the record and the settle otherwise refuses the + // run as active while the API already reports it ended. + finish(&state, run_id, status, error); // The view trails the terminal record; the aggregate reads the settled // projection, as the worker path reads the final state at worker exit. state.petri_projector.settle(run_id).await; @@ -507,7 +512,6 @@ pub(crate) async fn execute(state: Arc, run_id: RunId) { warn!(run_id = %run_id, error = ?err, "the run's final state could not be read for the usage aggregate"); } } - finish(&state, run_id, status, error); } /// Bring a Petri run the server left in flight back to its worker after a