diff --git a/crates/tui/src/tui/ui/apply.rs b/crates/tui/src/tui/ui/apply.rs index a41d58aa89..1de043a2f2 100644 --- a/crates/tui/src/tui/ui/apply.rs +++ b/crates/tui/src/tui/ui/apply.rs @@ -3626,7 +3626,22 @@ pub(crate) fn apply_loaded_session_with_goal( recovered_binding = tasks.session_store_binding(); } if recovered_binding.is_none() { - return Err("This session belongs to another Runtime host. Resume it in a new Codewhale process to reopen its saved store.".into()); + // Name the real condition and the path that actually works. The + // old wording ("resume it in a new Codewhale process") sent users + // in circles: starting a new process and then picking the session + // from `/resume` lands here again, because that is this same + // switch path. Opening the session *at launch* is a different + // route — `TaskManager::start` passes the saved binding through to + // `open_for_session`, which validates the existing store and + // adopts it (runtime_threads.rs, `validate_existing_store` then + // `open_inner`). So the advice has to say which one (#6207, #6225). + return Err(format!( + "This session's saved Runtime store belongs to a different host. \ + Switching to it from inside a running session cannot carry that \ + store's queued work across, but opening it directly can: run \ + `codewhale resume {}` from your shell.", + session.metadata.id + )); } } if app.session_transition_blocked() { diff --git a/crates/tui/src/tui/ui/tests/runtime_store_binding.rs b/crates/tui/src/tui/ui/tests/runtime_store_binding.rs index fac744477b..e947176ae0 100644 --- a/crates/tui/src/tui/ui/tests/runtime_store_binding.rs +++ b/crates/tui/src/tui/ui/tests/runtime_store_binding.rs @@ -281,7 +281,18 @@ async fn runtime_store_binding_survives_launch_snapshot_and_resume() -> anyhow:: let old_id = other_app.current_session_id.clone(); let error = apply_loaded_session_with_goal(&mut other_app, &mut resumed_config, &loaded, None) .unwrap_err(); - assert!(error.contains("Resume it in a new Codewhale process")); + // The refusal must name the route that actually works. "Resume it in a new + // Codewhale process" was true but unactionable: starting a new process and + // then picking the session from `/resume` returns here, because that is + // this same switch path (#6207, #6225). + assert!( + error.contains("codewhale resume"), + "the refusal must point at the direct-open path: {error}" + ); + assert!( + error.contains(&loaded.metadata.id), + "the refusal must name the session to open: {error}" + ); assert_eq!(other_app.current_session_id, old_id); assert_eq!(other_app.input, "preserve pending input"); foreign.shutdown_and_wait().await?;