From 4a89c0473259c2342ab2671f32a4654712e2c6d2 Mon Sep 17 00:00:00 2001 From: Hare Date: Sat, 12 Sep 2026 01:43:37 +0900 Subject: [PATCH] fix: allow corrupted workdir removal --- .../worker-runtime/src/working_directory.rs | 21 +++++++++++++ crates/workspace-server/src/server.rs | 31 +++++++++++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/crates/worker-runtime/src/working_directory.rs b/crates/worker-runtime/src/working_directory.rs index 4236ad7b..108ec4f7 100644 --- a/crates/worker-runtime/src/working_directory.rs +++ b/crates/worker-runtime/src/working_directory.rs @@ -3985,6 +3985,27 @@ mod tests { } } + #[test] + fn corrupted_working_directory_record_can_be_removed() { + let runtime_root = tempfile::tempdir().unwrap(); + let materializer = RuntimeGitMaterializer::new(runtime_root.path()); + let working_directory_id = "workdir-corrupted"; + let root = materializer.working_directory_root(working_directory_id); + fs::create_dir_all(root.join(CHECKOUT_DIR)).unwrap(); + fs::write(root.join(MATERIALIZATION_RECORD), b"not-json").unwrap(); + + let status = materializer + .working_directory_status(working_directory_id) + .unwrap(); + assert_eq!(status.summary.status, WorkingDirectoryStatusKind::Corrupted); + + let removed = materializer + .cleanup_working_directory(working_directory_id) + .unwrap(); + assert_eq!(removed.summary.status, WorkingDirectoryStatusKind::NotFound); + assert!(!root.exists()); + } + #[test] fn cleanup_working_directory_removes_clone_and_record() { let repo = create_clean_repo(); diff --git a/crates/workspace-server/src/server.rs b/crates/workspace-server/src/server.rs index dbbaaff1..528293ad 100644 --- a/crates/workspace-server/src/server.rs +++ b/crates/workspace-server/src/server.rs @@ -11425,8 +11425,10 @@ fn execute_reserved_workdir_removal_with_provider( true, ); }; - if status.summary.cleanliness.as_deref() != Some("clean") - || status.summary.status != WorkingDirectoryStatusKind::Active + let corrupted = status.summary.status == WorkingDirectoryStatusKind::Corrupted; + if !corrupted + && (status.summary.cleanliness.as_deref() != Some("clean") + || status.summary.status != WorkingDirectoryStatusKind::Active) { return api.config_store.complete_workdir_removal_retained( &operation, @@ -26684,6 +26686,31 @@ mod tests { assert_eq!(dirty.disposition, Some(WorkdirRemovalDisposition::Retained)); assert_eq!(dirty_provider.cleanup_calls(), 0); + let (corrupted_operation, mut corrupted_summary) = + reserve_removal_fixture(&api, "provider-corrupted"); + corrupted_summary.status = WorkingDirectoryStatusKind::Corrupted; + corrupted_summary.cleanliness = Some("unknown".to_string()); + let corrupted_provider = FakeWorkdirRemovalProvider::new( + workdir_removal_result( + WorkerOperationState::Accepted, + Some(corrupted_summary), + Vec::new(), + ), + workdir_removal_result(WorkerOperationState::Accepted, None, Vec::new()), + ); + let corrupted = execute_reserved_workdir_removal_with_provider( + &api, + corrupted_operation, + false, + &corrupted_provider, + ) + .unwrap(); + assert_eq!( + corrupted.disposition, + Some(WorkdirRemovalDisposition::Removed) + ); + assert_eq!(corrupted_provider.cleanup_calls(), 1); + let (unsupported_operation, unsupported_summary) = reserve_removal_fixture(&api, "provider-unsupported"); let unsupported_provider = FakeWorkdirRemovalProvider::new(