From bd517f2857a1c32519c40fb1a1631b6fc6f8e0b9 Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 13 May 2026 08:27:02 +0000 Subject: [PATCH] fix(warm-resume): send non-empty -p prompt with --resume so watchdog respawns can actually warm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit claude-code's --resume requires either: a) a deferred-tool marker in the resumed session (i.e. the prior session paused mid-tool-call), or b) a non-empty -p prompt to continue the conversation with. Watchdog-killed sessions have neither: the kill is asynchronous and leaves no deferred-tool marker, and our harness was passing an empty -p (because `resume_context_owned` is None for the common respawn case). claude-code then aborts with: "Error: No deferred tool marker found in the resumed session. Either the session was not deferred, the marker is stale (tool already ran), or it exceeds the tail-scan window. Provide a prompt to continue the conversation." The harness sees an aborted CLI with no session, prunes the recorded session_id, and respawns cold — paying the full prompt-cache miss for EVERY respawn. The new session_store logging (commit 0b50a624) made this 100% legible: every warm spawn we observed went `mode=warm` → crash → prune → `mode=cold` within a couple of seconds. Fix: when resuming with no failure-context to send, default the -p prompt to a brief "continue from PLAN.md" line. claude-code now has a valid continuation message and warm-resume should actually work. Co-Authored-By: Claude Opus 4.7 (1M context) --- server/src/agents/pool/start/spawn.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/server/src/agents/pool/start/spawn.rs b/server/src/agents/pool/start/spawn.rs index 4e4367aa..1dffc363 100644 --- a/server/src/agents/pool/start/spawn.rs +++ b/server/src/agents/pool/start/spawn.rs @@ -263,7 +263,18 @@ pub(super) async fn run_agent_spawn( Some(_) => { // Keep the full rendered prompt as fallback if resume fails. let fallback = prompt; - (resume_context_owned.unwrap_or_default(), Some(fallback)) + // claude-code's --resume requires a non-empty -p prompt unless the + // resumed session has a deferred-tool marker. Watchdog-killed + // sessions don't have one, so an empty -p aborts the CLI with + // "No deferred tool marker found... Provide a prompt to continue + // the conversation." We default to a brief "continue from + // PLAN.md" message when no gate-failure context was provided. + let resume_msg = resume_context_owned.filter(|s| !s.is_empty()).unwrap_or_else( + || { + "Continue from your prior session. Read PLAN.md for your current state and pick up where you left off.".to_string() + }, + ); + (resume_msg, Some(fallback)) } None => { if let Some(ctx) = resume_context_owned {