From d071c8df0016e0f11637084d39008d3807288b8d Mon Sep 17 00:00:00 2001 From: damocles Date: Thu, 8 Oct 2026 04:05:05 +0200 Subject: [PATCH] hive-subagent-mcp: cover the non-goal interrupted-turn case for #4337 --- hive-subagent-mcp/src/session.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hive-subagent-mcp/src/session.rs b/hive-subagent-mcp/src/session.rs index d8d09ed8..a931ab01 100644 --- a/hive-subagent-mcp/src/session.rs +++ b/hive-subagent-mcp/src/session.rs @@ -3870,6 +3870,7 @@ mod tests { StopReason::GoalReached(None), StopReason::NeedHelp("blocked".to_owned()), StopReason::TurnCap { turns: 5 }, + StopReason::Cancelled, ] { let extended = turn_end_summary("n", &TurnEnd::Complete, Some(&stop)); assert!( @@ -4295,6 +4296,18 @@ done // `finish_turn` has cleared its liveness clock too. until("the run ends", || state.last_event_age("n").is_none()).await; + // This `start` had no goal, so the fake claude's clean exit after + // SIGINT (`TurnEnd::Complete`) is exactly the case that used to + // report "finished: turn complete" with no sign it was interrupted. + // `plan_after_turn` checks `stop_reason` before it checks `goals`, so + // the recorded `Cancelled` survives even here — this is the + // non-goal case the goal-run-cancel fix left unverified. + assert_eq!( + state.stop_reason("n"), + Some(StopReason::Cancelled), + "a plain (non-goal) interrupted turn must still record why it stopped" + ); + assert!( !acp_session_file(&dir, "n").exists() && !dir.join("prompts.methods").exists(), "a claude agent's subagent never touches an ACP agent"