From 1c84b3ef1079be122a0bc7103bdea1f9e01868e8 Mon Sep 17 00:00:00 2001 From: damocles Date: Mon, 22 Jun 2026 23:19:40 +0200 Subject: [PATCH] fix(#1916): direct the agent to end its turn on graceful stop instead of a fenced-empty recv --- hive-ag3nt/src/mcp.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/hive-ag3nt/src/mcp.rs b/hive-ag3nt/src/mcp.rs index 6bbdeb60..d73fa4d5 100644 --- a/hive-ag3nt/src/mcp.rs +++ b/hive-ag3nt/src/mcp.rs @@ -84,10 +84,23 @@ impl From for SocketReply { hive_sh4re::Response::HostJournal { content } => Self::HostJournal(content), hive_sh4re::Response::Schedules { schedules } => Self::Schedules(schedules), hive_sh4re::Response::Containers { containers } => Self::Containers(containers), - // A graceful stop is pending — the inbox is fenced. If claude polls - // `recv` during its stop-checkpoint turn, present it as an empty - // inbox (the serve loop is the real handler of this signal). - hive_sh4re::Response::GracefulStop => Self::Messages(Vec::new()), + // A graceful stop is pending — the inbox is fenced. Returning an + // *empty* inbox here is ambiguous: claude's "park on recv" habit + // makes it long-poll again instead of ending the turn, so the + // stop-checkpoint turn never finishes and the drain wait times out + // into a hard stop. Return a single explicit directive instead, so + // every recv during the stop unmissably tells claude to flush + end. + hive_sh4re::Response::GracefulStop => Self::Messages(vec![hive_sh4re::DeliveredMessage { + from: "graceful-stop".into(), + body: "⛔ GRACEFUL STOP IN PROGRESS — the container shuts down as soon as this turn \ + ends. Flush anything worth keeping to your durable /state files, then END \ + YOUR TURN now. Do NOT call recv again: the inbox is fenced and recv will \ + only keep returning this same notice." + .into(), + id: 0, + redelivered: false, + in_reply_to: None, + }]), hive_sh4re::Response::AgentMeta { name, running,