From 534ce69adbc8da9e6c0f40839e86dacf3deec40b Mon Sep 17 00:00:00 2001 From: Dotta Date: Wed, 2 Sep 2026 22:59:09 -0500 Subject: [PATCH] fix(runner): stop after delivered terminal results --- .../crates/runner-core/src/durable/runner.rs | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/durable/runner.rs b/packages/paperclip-runner/runner/crates/runner-core/src/durable/runner.rs index 815cee93a9..3e25aae1bb 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/durable/runner.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/durable/runner.rs @@ -291,9 +291,21 @@ pub fn run_durable_runner( break; } } - if !disconnected && send_outbox(&mut transport, &state, &mut sent_source_seq).is_err() { - state.record_diagnostic("outbox delivery failed; unacknowledged suffix will replay"); - disconnected = true; + if !disconnected { + if let Err(error) = send_outbox(&mut transport, &state, &mut sent_source_seq) { + state.record_diagnostic( + "outbox delivery failed; unacknowledged suffix remains durable", + ); + if lifecycle_after_reply.durable_state().is_some() { + // The terminal result was delivered above. Never reconnect + // this process and overwrite its durable terminal state as + // ready merely to retry a later outbox frame. + store.save(&state)?; + let _ = executor.shutdown(); + return Err(error); + } + disconnected = true; + } } if let Some(durable_lifecycle) = lifecycle_after_reply .durable_state() @@ -382,16 +394,31 @@ pub fn run_durable_runner( durable_lifecycle, )?; } - let delivery = transport - .send_json(&command_result_envelope(&state, &result)) - .and_then(|()| send_outbox(&mut transport, &state, &mut sent_source_seq)); - if let Err(error) = delivery { + if let Err(error) = + transport.send_json(&command_result_envelope(&state, &result)) + { disconnected_since.get_or_insert_with(Instant::now); state.record_diagnostic(error.to_string()); state.reconnect_count = state.reconnect_count.saturating_add(1); store.save(&state)?; break; } + if let Err(error) = send_outbox(&mut transport, &state, &mut sent_source_seq) { + state.record_diagnostic( + "outbox delivery failed; unacknowledged suffix remains durable", + ); + store.save(&state)?; + if lifecycle.durable_state().is_some() { + // The controller has accepted this terminal result. + // Stop even though a later outbox frame failed so a + // reconnect cannot restore the runner to ready. + let _ = executor.shutdown(); + return Err(error); + } + disconnected_since.get_or_insert_with(Instant::now); + state.reconnect_count = state.reconnect_count.saturating_add(1); + break; + } if let Some(durable_lifecycle) = lifecycle.durable_state() { debug_assert_eq!(state.lifecycle, durable_lifecycle); executor.shutdown()?;