From 0c48c5dee31877273c36e9b03a4f4306a1fa5ac1 Mon Sep 17 00:00:00 2001 From: Hare Date: Sun, 6 Sep 2026 10:33:45 +0900 Subject: [PATCH] fix: fail closed without worker snapshots --- crates/worker-runtime/src/runtime.rs | 44 ++++++++++++++------------- crates/workspace-server/src/server.rs | 28 +++++++++++++++++ 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/crates/worker-runtime/src/runtime.rs b/crates/worker-runtime/src/runtime.rs index 76066336..e44e9f7f 100644 --- a/crates/worker-runtime/src/runtime.rs +++ b/crates/worker-runtime/src/runtime.rs @@ -1732,27 +1732,9 @@ impl Runtime { return Ok(snapshot); } } - Ok(protocol::Event::Snapshot { - session: protocol::SessionSnapshot { - pending_submissions: protocol::PendingSubmissionsSnapshot::default(), - entries: Vec::new(), - }, - greeting: protocol::Greeting { - worker_name: worker_ref.worker_id.to_string(), - cwd: String::new(), - provider: "worker-runtime".to_string(), - model: "worker-runtime".to_string(), - scope_summary: "runtime worker observation".to_string(), - tools: Vec::new(), - context_window: 0, - context_tokens: 0, - }, - state: protocol::WorkerStateSnapshot::initial(1), - in_flight: protocol::InFlightSnapshot { - blocks: Vec::new(), - commands: Vec::new(), - }, - internal_workers: Vec::new(), + Err(RuntimeError::WorkerExecutionUnavailable { + worker_id: worker_ref.worker_id, + message: "authoritative Worker snapshot is unavailable".to_string(), }) } @@ -4974,6 +4956,26 @@ mod tests { } } + #[cfg(feature = "ws-server")] + #[test] + fn observation_snapshot_fails_closed_when_backend_snapshot_is_unavailable() { + let runtime = runtime_with_backend(); + let detail = runtime + .create_worker(task_request("snapshot unavailable")) + .unwrap(); + + assert!(matches!( + runtime + .worker_observation_snapshot(&detail.worker_ref) + .unwrap_err(), + RuntimeError::WorkerExecutionUnavailable { + worker_id, + message, + } if worker_id == detail.worker_ref.worker_id + && message == "authoritative Worker snapshot is unavailable" + )); + } + struct InputOnlyBackend; impl WorkerExecutionBackend for InputOnlyBackend { diff --git a/crates/workspace-server/src/server.rs b/crates/workspace-server/src/server.rs index 9cbeb899..8b70d092 100644 --- a/crates/workspace-server/src/server.rs +++ b/crates/workspace-server/src/server.rs @@ -18865,6 +18865,34 @@ mod tests { ) } + fn worker_snapshot( + &self, + handle: &worker_runtime::execution::WorkerExecutionHandle, + ) -> Option { + Some(protocol::Event::Snapshot { + session: protocol::SessionSnapshot { + pending_submissions: protocol::PendingSubmissionsSnapshot::default(), + entries: Vec::new(), + }, + greeting: protocol::Greeting { + worker_name: handle.worker_ref().worker_id.to_string(), + cwd: String::new(), + provider: "deterministic-workspace-server-test".to_string(), + model: "deterministic-workspace-server-test".to_string(), + scope_summary: "test execution snapshot".to_string(), + tools: Vec::new(), + context_window: 0, + context_tokens: 0, + }, + state: protocol::WorkerStateSnapshot::initial(1), + in_flight: protocol::InFlightSnapshot { + blocks: Vec::new(), + commands: Vec::new(), + }, + internal_workers: Vec::new(), + }) + } + fn stop_worker( &self, _handle: &worker_runtime::execution::WorkerExecutionHandle,