Skip to content

[codex] Fix worktree delete cleanup ordering#4801

Merged
Kitenite merged 6 commits into
mainfrom
fix-worktree-deletion-err
May 22, 2026
Merged

[codex] Fix worktree delete cleanup ordering#4801
Kitenite merged 6 commits into
mainfrom
fix-worktree-deletion-err

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented May 21, 2026

What changed

  • Routes the legacy workspace.delete endpoint through the v2 workspaceCleanup.destroy saga so desktop, CLI, SDK, and MCP deletes share one cleanup path.
  • Reorders workspace cleanup so local terminal/worktree cleanup happens before cloud deletion; cloud delete now happens only after the worktree is gone or already missing.
  • Keeps post-cloud host sqlite cleanup best-effort, surfaces cleanup warnings to CLI/SDK/MCP callers, and adds regression coverage for failed worktree removal.

Why

Some delete callers were using the legacy workspace.delete endpoint, which deleted the cloud row first, attempted plain worktree removal, swallowed removal errors, and then deleted the local row. That could make a workspace disappear from the app while leaving the worktree on disk.

The desktop app should not remove the authoritative cloud row until local worktree removal succeeds. If local removal fails while the path still exists, the delete now fails before cloud delete so the workspace can reappear and be retried.

Validation

  • bun test packages/host-service/test/workspace-cleanup.test.ts packages/host-service/test/integration/workspace-create-delete.integration.test.ts packages/host-service/test/integration/bug-hunt-2.integration.test.ts packages/host-service/test/integration/bug-hunt-3.integration.test.ts
  • bun run lint:fix
  • bun run lint
  • bun run --cwd packages/host-service typecheck
  • bun run --cwd packages/cli typecheck
  • bun run --cwd packages/mcp-v2 typecheck
  • bun run --cwd packages/sdk typecheck
  • git diff --check

Open in Stage

Summary by cubic

Fixes workspace deletion by removing local worktrees before cloud delete and routing all delete entry points through the v2 cleanup path. Calls now return structured results and surface warnings to prevent “vanishing” workspaces and orphaned disk state.

  • Bug Fixes
    • Routed legacy workspace.delete through exported destroyWorkspace with an in‑flight guard; v1 callers use force and return success, cloudDeleted, worktreeRemoved, branchDeleted, and warnings.
    • Enforced order: preflight → teardown → PTYs/worktree (double --force) → cloud delete → branch delete → host sqlite; pre‑validates cloud API before touching disk.
    • Abort before cloud delete if the repo can’t open or worktree removal fails while the path exists; if the project row is missing, skip worktree removal and warn; if the dir is already gone, treat it as removed.
    • If cloud delete fails after local removal, keep the local row so the workspace stays visible and retryable; branch‑delete failures are warnings.
    • CLI prints per‑workspace warnings in output; SDK/MCP result types updated; tests cover locked worktrees, teardown failures requiring force, cloud‑delete failure retries, branch delete after cloud, and missing project metadata.

Written for commit 5be1caf. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • New Features

    • Deletion returns richer results (success plus cloud/worktree/branch status and warnings); CLI shows a formatted Warnings section when present.
  • Bug Fixes

    • Cleanup sequencing improved for more consistent local/cloud/DB outcomes.
    • Force-delete now removes locked worktrees.
    • Branch-delete failures and certain post-delete errors are reported as warnings instead of failing the whole operation.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 34a506e7-6f66-4fe6-a908-d29f459efd70

📥 Commits

Reviewing files that changed from the base of the PR and between b131e0f and 5be1caf.

📒 Files selected for processing (3)
  • packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts
  • packages/host-service/test/workspace-cleanup.test.ts
  • packages/mcp-v2/src/tools/workspaces/delete.ts

📝 Walkthrough

Walkthrough

Workspace deletion is refactored to extract a new destroyWorkspace function with explicit input/output types and reordered phases: local cleanup (with strict worktree-removal failure handling) runs before cloud deletion, ensuring clearer failure semantics. The router delegates to this function; the CLI and MCP tool consume enriched result fields (worktreeRemoved, warnings). Tests and integrations verify ordering, force, and retry scenarios.

Changes

Workspace deletion refactor: phase reordering and result enrichment

Layer / File(s) Summary
Result type contract and input shape
packages/sdk/src/resources/workspaces.ts, packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts, packages/host-service/src/trpc/router/workspace-cleanup/index.ts
WorkspaceDeleteResult is redefined with explicit success, worktreeRemoved?, cloudDeleted?, branchDeleted?, and warnings? fields, removing the open index signature. A new exported DestroyWorkspaceInput interface specifies deletion input parameters. destroyWorkspace is exported from the workspace-cleanup module.
Destroy function extraction and phase reordering
packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts
destroyWorkspace is extracted as an exported function that applies the in-flight guard and delegates to runDestroy. The deletion sequence now validates cloud API availability, performs local cleanup (worktree removal, optional branch deletion) first, then performs the cloud delete, and finally host sqlite cleanup. Worktree-removal failure handling and existence re-checks were made stricter.
Router deletion handler delegation
packages/host-service/src/trpc/router/workspace/workspace.ts
workspaceRouter.delete now delegates to destroyWorkspace(ctx, { id, force: true, deleteBranch: false }) and returns its result; previous inline cloud/local deletion and cache invalidation logic was removed and imports updated.
CLI warning aggregation and display
packages/cli/src/commands/workspaces/delete/command.ts
The CLI deletion command aggregates per-workspace warnings returned from the host into a top-level warnings array and appends a formatted "Warnings" section to the user-facing message when warnings are present; the command's returned data includes warnings.
MCP tool return type expansion
packages/mcp-v2/src/tools/workspaces/delete.ts
The workspaces_delete tool's hostServiceCall return type is expanded to include worktreeRemoved, cloudDeleted, branchDeleted, and warnings: string[] alongside success.
Test infrastructure and assertion updates
packages/host-service/test/workspace-cleanup.test.ts, packages/host-service/test/integration/bug-hunt-2.integration.test.ts, packages/host-service/test/integration/bug-hunt-3.integration.test.ts, packages/host-service/test/integration/workspace-create-delete.integration.test.ts, packages/host-service/test/integration/workspace-cleanup.integration.test.ts
Test context builders gain optional worktreeRemove() and branchDelete() hooks and a noApi flag; assertions across unit and integration tests now check individual result fields (success, worktreeRemoved, warnings). The workspace-cleanup test suite is reorganized into cleanup-ordering tests; integration tests add force/remove/teardown/cloud-failure retry scenarios and lifecycle cleanup helpers.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 With paws that nudge the code just right,
I hop through worktrees late at night,
I tidy branches, whisper "done",
Warnings gathered, one by one,
Now deletions finish soft and bright.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title '[codex] Fix worktree delete cleanup ordering' directly reflects the main change: reordering workspace cleanup so local worktree removal happens before cloud deletion.
Description check ✅ Passed The pull request description covers the key sections: what changed, why it matters, and validation steps. While the description template includes sections like 'Related Issues' and 'Type of Change', the author provided a comprehensive custom description that clearly explains the changes and their rationale.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-worktree-deletion-err

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@capy-ai
Copy link
Copy Markdown

capy-ai Bot commented May 21, 2026

Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

@stage-review
Copy link
Copy Markdown

stage-review Bot commented May 21, 2026

Ready to review this PR? Stage has broken it down into 5 individual chapters for you:

Title
1 Update workspace delete result types
2 Reorder workspace cleanup phases and logic
3 Route legacy delete through v2 cleanup
4 Update CLI and MCP to handle warnings
5 Verify cleanup ordering and failure modes
Open in Stage

Chapters generated by Stage for commit 5be1caf on May 21, 2026 11:23pm UTC.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts (1)

224-257: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Check cloud API precondition before running teardown.

Line 228 can execute teardown side effects before Line 252 rejects on missing ctx.api. That means deletion can fail after local teardown has already run. Move the precondition check ahead of teardown to keep failure atomic.

Suggested fix
 	// ─── Step 0: Preflight ─────────────────────────────────────────
 	// Block only on dirty worktree (the common "I forgot to commit"
 	// case). Missing/broken local state is handled by the cleanup phase.
 	if (!input.force && local && project) {
 		try {
 			const git = await ctx.git(local.worktreePath);
 			const status = await git.status();
 			if (!status.isClean()) {
 				throw new TRPCError({
 					code: "CONFLICT",
 					message: "Worktree has uncommitted changes",
 				});
 			}
 		} catch (err) {
 			if (err instanceof TRPCError) throw err;
 			// Can't read status (missing worktree dir, etc.) — not a
 			// conflict. Continue; step 3b will skip idempotently.
 		}
 	}
 
+	// Ensure we can commit to cloud before any local teardown/cleanup side effects.
+	if (!ctx.api) {
+		throw new TRPCError({
+			code: "PRECONDITION_FAILED",
+			message: "Cloud API not configured",
+		});
+	}
+
 	// ─── Step 1: Teardown ──────────────────────────────────────────
 	// Script is the user's last chance to stop services / flush state
 	// before the workspace goes away. Failure here is recoverable
 	// via force-retry, which skips this step.
 	if (!input.force && local && project) {
 		const teardown: TeardownResult = await runTeardown({
 			db: ctx.db,
 			workspaceId: input.workspaceId,
 			worktreePath: local.worktreePath,
 		});
 		if (teardown.status === "failed") {
 			const cause: TeardownFailureCause = {
 				kind: "TEARDOWN_FAILED",
 				exitCode: teardown.exitCode,
 				signal: teardown.signal,
 				timedOut: teardown.timedOut,
 				outputTail: teardown.outputTail,
 			};
 			throw new TRPCError({
 				code: "INTERNAL_SERVER_ERROR",
 				message: "Teardown script failed",
 				cause,
 			});
 		}
 	}
-
-	// Make sure we can commit to cloud before touching local disk. The actual
-	// cloud mutation happens after the worktree is removed.
-	if (!ctx.api) {
-		throw new TRPCError({
-			code: "PRECONDITION_FAILED",
-			message: "Cloud API not configured",
-		});
-	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts`
around lines 224 - 257, The cloud API precondition (ctx.api) must be validated
before running teardown to avoid performing local side effects when cloud
commits will later fail; move the existing check that throws the TRPCError with
code "PRECONDITION_FAILED" so it executes before the teardown block (i.e.,
before calling runTeardown), keeping the same error shape and message, and
preserve existing conditions (input.force, local, project) so you still skip
teardown when forced or non-local/no-project; ensure references to runTeardown,
ctx.api, TRPCError, input.force, local, and project are updated accordingly.
🧹 Nitpick comments (1)
packages/mcp-v2/src/tools/workspaces/delete.ts (1)

21-38: ⚡ Quick win

Inconsistent return type shapes.

The handler returns two different shapes:

  • Early return (line 22): { success: true, alreadyGone: true }
  • Normal return (lines 24-28): { success: boolean; worktreeRemoved: boolean; warnings: string[] }

This creates a union type that forces callers to distinguish between the two cases. For API consistency, consider adding the enriched fields to the early return path with default values:

♻️ Proposed fix for consistent return shape
 		if (!workspace) {
-			return { success: true, alreadyGone: true };
+			return {
+				success: true,
+				alreadyGone: true,
+				worktreeRemoved: false,
+				warnings: [],
+			};
 		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mcp-v2/src/tools/workspaces/delete.ts` around lines 21 - 38, The
early-return when workspace is missing returns { success: true, alreadyGone:
true } which has a different shape than the hostServiceCall response; update the
early return in the delete handler to include the same fields returned by
hostServiceCall (success, worktreeRemoved, warnings) plus alreadyGone flag (e.g.
success: true, worktreeRemoved: false, warnings: [], alreadyGone: true) so
callers always receive a consistent object shape; locate the check using
workspace and input.id in this file and adjust that return accordingly to match
the hostServiceCall shape.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts`:
- Around line 224-257: The cloud API precondition (ctx.api) must be validated
before running teardown to avoid performing local side effects when cloud
commits will later fail; move the existing check that throws the TRPCError with
code "PRECONDITION_FAILED" so it executes before the teardown block (i.e.,
before calling runTeardown), keeping the same error shape and message, and
preserve existing conditions (input.force, local, project) so you still skip
teardown when forced or non-local/no-project; ensure references to runTeardown,
ctx.api, TRPCError, input.force, local, and project are updated accordingly.

---

Nitpick comments:
In `@packages/mcp-v2/src/tools/workspaces/delete.ts`:
- Around line 21-38: The early-return when workspace is missing returns {
success: true, alreadyGone: true } which has a different shape than the
hostServiceCall response; update the early return in the delete handler to
include the same fields returned by hostServiceCall (success, worktreeRemoved,
warnings) plus alreadyGone flag (e.g. success: true, worktreeRemoved: false,
warnings: [], alreadyGone: true) so callers always receive a consistent object
shape; locate the check using workspace and input.id in this file and adjust
that return accordingly to match the hostServiceCall shape.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bc878255-3f48-494b-a1e1-d9a34f6f0afe

📥 Commits

Reviewing files that changed from the base of the PR and between 2384317 and 44e370a.

📒 Files selected for processing (10)
  • packages/cli/src/commands/workspaces/delete/command.ts
  • packages/host-service/src/trpc/router/workspace-cleanup/index.ts
  • packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts
  • packages/host-service/src/trpc/router/workspace/workspace.ts
  • packages/host-service/test/integration/bug-hunt-2.integration.test.ts
  • packages/host-service/test/integration/bug-hunt-3.integration.test.ts
  • packages/host-service/test/integration/workspace-create-delete.integration.test.ts
  • packages/host-service/test/workspace-cleanup.test.ts
  • packages/mcp-v2/src/tools/workspaces/delete.ts
  • packages/sdk/src/resources/workspaces.ts

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 21, 2026

Greptile Summary

This PR fixes a class of workspace delete bugs where cloud rows were removed before local worktree cleanup, leaving disk state orphaned when cleanup failed. It consolidates all delete callers (legacy workspace.delete, CLI, SDK, MCP) through a single destroyWorkspace saga and reorders the phases so local teardown precedes cloud delete.

  • The cleanup sequence is reordered: PTY disposal → worktree removal → (branch delete) → cloud delete → sqlite row. Worktree removal failure while the path still exists now throws and blocks cloud delete, so the workspace stays visible and the delete is retryable.
  • The legacy workspace.delete endpoint is now a thin shim over destroyWorkspace with force: true and deleteBranch: false, gaining the in-flight concurrency guard and structured warning output; CLI and MCP callers are updated to consume the new result shape.

Confidence Score: 3/5

The worktree-before-cloud reordering is sound for the main case, but branch deletion was also moved before cloud delete, which can leave a workspace visible in the app without its branch if the cloud call fails transiently.

The core ordering fix is correct and well-tested. The concern is that branch deletion (step 2c) now runs before cloud delete (step 3). When deleteBranch is true and cloud delete fails transiently, the workspace reappears in the sidebar but the branch pointer is already gone, making the workspace inaccessible until a force-retry removes it. The old design had branch deletion as a best-effort step after the cloud commit point, so this is a step backward for that specific failure scenario. The coverage gap for git-factory failure with an existing worktree path is also worth addressing before merge.

packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts — branch deletion ordering around step 2c/3; packages/host-service/test/workspace-cleanup.test.ts — missing coverage for git-factory failure with existing path.

Important Files Changed

Filename Overview
packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts Core cleanup saga reordered: local teardown now precedes cloud delete, worktree failure now throws instead of warning, but branch deletion was moved before the cloud commit point which can leave a workspace visible in the app with its branch already deleted on cloud API failure.
packages/host-service/src/trpc/router/workspace/workspace.ts Legacy workspace.delete now delegates to destroyWorkspace with force:true and deleteBranch:false, sharing one cleanup path with v2; main-workspace protection preserved via isMainWorkspace.
packages/host-service/test/workspace-cleanup.test.ts Replaced best-effort warning test with new ordering test; stale "phase 2" label remains in in-flight guard test; git-factory-throw-with-path-present scenario is not covered.
packages/cli/src/commands/workspaces/delete/command.ts CLI now surfaces per-workspace warnings from the structured delete result; clean and correct.
packages/mcp-v2/src/tools/workspaces/delete.ts Type annotation updated to include worktreeRemoved and warnings fields; matches the new return shape.
packages/sdk/src/resources/workspaces.ts WorkspaceDeleteResult interface replaced catch-all with named optional fields; all fields optional so existing callers won't break.
packages/host-service/test/integration/bug-hunt-3.integration.test.ts Test updated to verify legacy workspace.delete routes through v2 saga and force-removes dirty worktrees; assertions updated to new return shape.

Comments Outside Diff (1)

  1. packages/host-service/test/workspace-cleanup.test.ts, line 293 (link)

    P2 Stale phase reference in test label — after the reorder, cloud delete is now step 3, not step 2. Keeping the old label makes the test suite misleading for anyone debugging a future failure here.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/host-service/test/workspace-cleanup.test.ts
    Line: 293
    
    Comment:
    Stale phase reference in test label — after the reorder, cloud delete is now step 3, not step 2. Keeping the old label makes the test suite misleading for anyone debugging a future failure here.
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts:322-336
**Branch deletion before the cloud commit point**

Step 2c (branch deletion) now runs before step 3 (cloud delete). If `deleteBranch: true` and cloud delete fails after a successful branch deletion, the workspace remains visible in the app but the branch is permanently gone. The user sees the workspace in the sidebar, tries to check out or interact with it, and finds the branch missing — with no indication it was already deleted. On retry, the branch-not-found failure is demoted to a warning so cloud delete eventually succeeds, but by then the branch pointer is irretrievably lost (without manual `git reflog` surgery).

The old design had branch deletion in step 3c (after the cloud commit point), where it was safely best-effort. Moving it before cloud delete means a cloud API transient failure converts an optional cleanup into an unrecoverable loss of the branch reference.

### Issue 2 of 3
packages/host-service/test/workspace-cleanup.test.ts:293
Stale phase reference in test label — after the reorder, cloud delete is now step 3, not step 2. Keeping the old label makes the test suite misleading for anyone debugging a future failure here.

```suggestion
	test("clears the Set when phase 3 (cloud delete) throws", async () => {
```

### Issue 3 of 3
packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts:282-297
**Missing test for git-factory failure when the worktree path still exists**

The removed test ("git-factory failure in phase 3 becomes a warning") used a non-existent `worktreePath`, so `worktreeRemoved` was `true` at the start and the git factory was never invoked. That scenario is now unreachable for `deleteBranch: false`.

The new hard-error path — git factory throws and `existsSync(local.worktreePath)` is `true` — has no explicit unit test. The added "worktree removal failure" test covers the mock `worktreeRemove` throw but leaves git-factory-throw-with-path-present uncovered. A test that sets `gitFactoryThrows: true` on a real temp directory (similar to the new ordering test) would pin this behavior.

Reviews (1): Last reviewed commit: "Fix worktree delete cleanup ordering" | Re-trigger Greptile

Comment on lines +322 to +336
@@ -335,7 +332,10 @@ async function runDestroy(ctx: HostServiceContext, input: DestroyInput) {
}
}

// 3d. Host sqlite row
// ─── Step 3: Cloud delete ──────────────────────────────────────
await ctx.api.v2Workspace.delete.mutate({ id: input.workspaceId });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Branch deletion before the cloud commit point

Step 2c (branch deletion) now runs before step 3 (cloud delete). If deleteBranch: true and cloud delete fails after a successful branch deletion, the workspace remains visible in the app but the branch is permanently gone. The user sees the workspace in the sidebar, tries to check out or interact with it, and finds the branch missing — with no indication it was already deleted. On retry, the branch-not-found failure is demoted to a warning so cloud delete eventually succeeds, but by then the branch pointer is irretrievably lost (without manual git reflog surgery).

The old design had branch deletion in step 3c (after the cloud commit point), where it was safely best-effort. Moving it before cloud delete means a cloud API transient failure converts an optional cleanup into an unrecoverable loss of the branch reference.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts
Line: 322-336

Comment:
**Branch deletion before the cloud commit point**

Step 2c (branch deletion) now runs before step 3 (cloud delete). If `deleteBranch: true` and cloud delete fails after a successful branch deletion, the workspace remains visible in the app but the branch is permanently gone. The user sees the workspace in the sidebar, tries to check out or interact with it, and finds the branch missing — with no indication it was already deleted. On retry, the branch-not-found failure is demoted to a warning so cloud delete eventually succeeds, but by then the branch pointer is irretrievably lost (without manual `git reflog` surgery).

The old design had branch deletion in step 3c (after the cloud commit point), where it was safely best-effort. Moving it before cloud delete means a cloud API transient failure converts an optional cleanup into an unrecoverable loss of the branch reference.

How can I resolve this? If you propose a fix, please make it concise.

Comment thread packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts Outdated
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 10 files

Re-trigger cubic

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 21, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch

Thank you for your contribution! 🎉

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts (1)

277-319: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail closed when the local row exists but the project row is missing.

This block only protects the cloud delete when local && project is present. If local exists without project, Step 2 skips the existsSync(local.worktreePath) check entirely, but Step 3 still deletes the cloud row and Step 5 removes the sqlite row. That reintroduces the orphaned-worktree case this PR is fixing whenever local metadata is partially missing. Treat local && !project && worktreePath still exists as a hard stop; only continue if the worktree is already gone.

Suggested guard
 let worktreeRemoved = false;
 let branchDeleted = false;
 let git: Awaited<ReturnType<typeof ctx.git>> | null = null;
+if (local && !project) {
+	worktreeRemoved = !existsSync(local.worktreePath);
+	if (!worktreeRemoved) {
+		throw new TRPCError({
+			code: "INTERNAL_SERVER_ERROR",
+			message: `Missing project metadata for workspace ${input.workspaceId}; refusing cloud delete while worktree still exists at ${local.worktreePath}`,
+		});
+	}
+}
 if (local && project) {
 	worktreeRemoved = !existsSync(local.worktreePath);
 	try {
 		git = await ctx.git(project.repoPath);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts`
around lines 277 - 319, The current logic only runs the
existsSync(local.worktreePath) guard when both local and project are present,
which lets the cleanup proceed (and delete cloud/sql rows) when local exists but
project is missing; change the flow so that if local exists and project is
null/undefined you still check the filesystem and abort unless the worktree is
already gone: when local && !project, compute worktreeRemoved =
!existsSync(local.worktreePath) and if worktreeRemoved is false throw a
TRPCError (same shape as other errors) instead of continuing; keep the existing
warnings/ctx.git flow for the local && project case (references: local, project,
worktreeRemoved, existsSync, ctx.git, local.worktreePath, TRPCError, warnings).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/mcp-v2/src/tools/workspaces/delete.ts`:
- Around line 22-27: The early-return object in deleteWorkspace (or the delete
flow in packages/mcp-v2/src/tools/workspaces/delete.ts) returns success,
alreadyGone, worktreeRemoved, and warnings but omits cloudDeleted and
branchDeleted; update that early-return to include cloudDeleted: false (or
appropriate boolean) and branchDeleted: false so the returned shape matches the
normal deletion path and the PR summary’s five-field contract.

---

Outside diff comments:
In
`@packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts`:
- Around line 277-319: The current logic only runs the
existsSync(local.worktreePath) guard when both local and project are present,
which lets the cleanup proceed (and delete cloud/sql rows) when local exists but
project is missing; change the flow so that if local exists and project is
null/undefined you still check the filesystem and abort unless the worktree is
already gone: when local && !project, compute worktreeRemoved =
!existsSync(local.worktreePath) and if worktreeRemoved is false throw a
TRPCError (same shape as other errors) instead of continuing; keep the existing
warnings/ctx.git flow for the local && project case (references: local, project,
worktreeRemoved, existsSync, ctx.git, local.worktreePath, TRPCError, warnings).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: db17cab0-ac7e-4ae2-9ce2-6434b2ae375a

📥 Commits

Reviewing files that changed from the base of the PR and between 0de9972 and b131e0f.

📒 Files selected for processing (4)
  • packages/host-service/src/trpc/router/workspace-cleanup/workspace-cleanup.ts
  • packages/host-service/test/integration/workspace-cleanup.integration.test.ts
  • packages/host-service/test/workspace-cleanup.test.ts
  • packages/mcp-v2/src/tools/workspaces/delete.ts

Comment thread packages/mcp-v2/src/tools/workspaces/delete.ts
@Kitenite Kitenite merged commit 59a2a34 into main May 22, 2026
17 checks passed
@Kitenite Kitenite deleted the fix-worktree-deletion-err branch May 22, 2026 00:03
@saddlepaddle saddlepaddle mentioned this pull request May 22, 2026
3 tasks
MocA-Love added a commit to MocA-Love/superset that referenced this pull request May 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant