Skip to content

fix(desktop): auto-repair worktree path when directory is moved/unnested#2060

Open
Kitenite wants to merge 7 commits intomainfrom
kitenite/rust-shield
Open

fix(desktop): auto-repair worktree path when directory is moved/unnested#2060
Kitenite wants to merge 7 commits intomainfrom
kitenite/rust-shield

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Mar 5, 2026

Summary

  • auto-detect and repair stale worktree paths before they reach renderer workspace queries
  • propagate repaired paths through terminal/status endpoints that still read worktree paths directly
  • invalidate workspace path queries after terminal attach so the UI stops using stale cached paths

Why / Context

When a worktree directory is moved or unnested, the absolute path stored in local SQLite can go stale. The original PR fixed terminal attach and refreshGitStatus, but renderer flows like the workspace page and sidebar still received the stale worktreePath from workspaces.get / getAllGrouped and passed it into changes/status queries.

That meant the path repair happened too late: some UI still broke until an unrelated refetch happened.

What Changed

Shared path resolver

Added resolveWorktreePathWithRepair() in apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.ts.

It returns:

  1. the stored path if it still exists
  2. the repaired path if tryRepairWorktreePath() succeeds
  3. null if the worktree still cannot be resolved

Renderer-facing workspace queries

These now repair stale worktree paths before returning them to the renderer:

  • workspaces.get
  • workspaces.getAllGrouped

This is the key fix for the actual UI path that powers Changes / Files / tab content.

Remaining worktree-path endpoints

These now use the same shared resolver:

  • terminal.createOrAttach
  • terminal.getWorkspaceCwd
  • workspaces.refreshGitStatus
  • workspaces.getGitHubStatus
  • workspaces.getWorktreeInfo

Frontend cache propagation

After a successful terminal createOrAttach, invalidate:

  • workspaces.get({ id })
  • workspaces.getAllGrouped()
  • terminal.getWorkspaceCwd(workspaceId)

This prevents the renderer from continuing to use a stale cached path after the DB row has been repaired.

Limitation

Auto-repair still depends on git worktree list knowing the new path. So it works when:

  • git worktree move was used
  • git worktree repair was run after a manual move

If Git itself still reports the stale path, the app cannot discover the new location automatically.

Manual QA Checklist

  • Workspace page loads with repaired path after git worktree move
  • Changes / Files views work without first opening a terminal
  • Sidebar workspace entries receive the repaired worktreePath
  • Terminal reconnects after move/unnest
  • GitHub PR status still resolves after repair
  • If repair fails, existing missing-path behavior remains intact

Testing

  • bun test apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.test.ts
  • bun run --filter @superset/desktop typecheck
  • bunx biome check --write ...

When a worktree directory is moved or unnested, the stored path in the
local database becomes stale, causing terminal connections to fail with
"Workspace path does not exist" errors. This adds auto-detection of the
new path by querying `git worktree list` and matching by branch name,
then updating the database so the terminal (and git status) can reconnect.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 5, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a worktree path repair utility and integrates it into terminal createOrAttach and git-status refresh flows to detect and repair stale or moved worktree paths by consulting the database, filesystem, and repo branch worktree resolution.

Changes

Cohort / File(s) Summary
Worktree Repair Utility
apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.ts
New tryRepairWorktreePath(worktreeId) that loads the worktree, checks on-disk existence, resolves branch worktree path from the main repo, updates the DB if changed, and returns the repaired path or null.
Repair Tests
apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.test.ts
New integration-style tests using real git repos and an in-memory/mock DB to validate repair, non-repair, and edge cases (missing worktree/project, main repo equality, not found by git).
Terminal Integration
apps/desktop/src/lib/trpc/routers/terminal/terminal.ts
Makes workspacePath mutable and invokes tryRepairWorktreePath for worktree-type workspaces when the resolved path is missing; continues existing assertWorkspaceUsable checks with the possibly repaired path.
Git Status Integration
apps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.ts
When a worktree path is missing, calls tryRepairWorktreePath(worktree.id), uses the returned repaired path (if any) for ahead/behind and status calculations, and throws NOT_FOUND if still missing.
Imports / FS checks
apps/desktop/src/lib/trpc/routers/terminal/terminal.ts, apps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.ts
Adds tryRepairWorktreePath import and filesystem existence checks (e.g., existsSync) to enable the repair flow.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Router as Terminal/GitStatus Router
    participant Repair as tryRepairWorktreePath
    participant DB as Database
    participant FS as Filesystem
    participant Git as getBranchWorktreePath

    Client->>Router: createOrAttach / refreshGitStatus request
    Router->>FS: check stored worktree path
    FS-->>Router: path missing
    Router->>Repair: tryRepairWorktreePath(worktreeId)
    Repair->>DB: fetch worktree & project
    DB-->>Repair: worktree + mainRepoPath
    Repair->>Git: getBranchWorktreePath(mainRepoPath, branch)
    Git-->>Repair: resolved path
    Repair->>FS: verify resolved path exists
    FS-->>Repair: path exists
    Repair->>DB: update worktree path
    DB-->>Repair: updated record
    Repair-->>Router: repaired path (or null)
    Router->>Router: continue operation using repaired path (if any)
    Router-->>Client: operation result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I sniffed the branches and followed the trail,
Through moved directories and an old stale tale.
I nudged lost paths back into place,
Tucked worktrees gently in their space,
A happy hop — repositories prevail! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely describes the main change: auto-repairing stale worktree paths when directories are moved or unnested, which directly addresses the core functionality added.
Description check ✅ Passed The pull request description is comprehensive and well-structured, covering all required template sections with detailed explanations of changes, context, testing, and a manual QA checklist.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kitenite/rust-shield

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.

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 3 files

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.ts`:
- Around line 67-76: After attempting tryRepairWorktreePath(worktree.id) you
must re-check that the resolved worktreePath exists before calling
getAheadBehindCount; if the repairedPath is falsy or the filesystem still lacks
the path, throw a deterministic tRPC error (e.g., via trpcError or TRPCError)
instead of calling getAheadBehindCount. Update the block that sets worktreePath
(and the surrounding guard) to perform a second existsSync check after repair
and raise a clear tRPC error mentioning the worktree.id when the path is still
missing so downstream git calls never run against a non-existent path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b33889fb-433f-45b4-b8ce-bbaa26cfa528

📥 Commits

Reviewing files that changed from the base of the PR and between bd56a7e and d8a4c52.

📒 Files selected for processing (3)
  • apps/desktop/src/lib/trpc/routers/terminal/terminal.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.ts

Kitenite added 2 commits March 4, 2026 22:32
`git worktree list` includes the main worktree. If the stale worktree's
branch happens to be checked out in the main repo, the repair would
incorrectly rebind the worktree row to mainRepoPath. Guard against this
by comparing the candidate path against the project's main repo path.
… tests

- Replace resolve() with realpathSync() to canonicalize symlinks
  (e.g. /var → /private/var on macOS) when comparing candidate path
  against main repo path.
- Add 6 regression tests covering: valid path no-op, successful repair
  after git worktree move, main repo rejection, missing project/worktree,
  and branch not found by git.
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.

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.test.ts">

<violation number="1" location="apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.test.ts:185">
P2: This test is non-deterministic across Git configurations because it hardcodes `branch: "main"` after `git init`, so it may pass without exercising the intended rejection path.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.test.ts Outdated
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.

🧹 Nitpick comments (1)
apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.test.ts (1)

34-40: Consider splitting shell commands for better cross-platform compatibility.

The chained shell command with single quotes ('init') could fail on Windows. While this may not be a concern if development is Unix-only, splitting into separate execSync calls would be more robust.

♻️ Optional refactor for portability
 function seedCommit(repoPath: string): void {
 	writeFileSync(join(repoPath, "README.md"), "# test\n");
-	execSync("git add . && git commit -m 'init'", {
-		cwd: repoPath,
-		stdio: "ignore",
-	});
+	execSync("git add .", { cwd: repoPath, stdio: "ignore" });
+	execSync('git commit -m "init"', { cwd: repoPath, stdio: "ignore" });
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.test.ts`
around lines 34 - 40, The test helper seedCommit uses a single execSync call
with a chained shell command and single quotes which can break on Windows; split
the operations inside seedCommit into two execSync calls (one for "git add ."
and one for "git commit -m \"init\"" or an equivalent cross-platform commit
invocation) so the commands are executed separately and avoid single-quote
quoting issues when running on Windows environments.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.test.ts`:
- Around line 34-40: The test helper seedCommit uses a single execSync call with
a chained shell command and single quotes which can break on Windows; split the
operations inside seedCommit into two execSync calls (one for "git add ." and
one for "git commit -m \"init\"" or an equivalent cross-platform commit
invocation) so the commands are executed separately and avoid single-quote
quoting issues when running on Windows environments.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 521558a6-139c-45ba-ac6b-961337a550a2

📥 Commits

Reviewing files that changed from the base of the PR and between 0fc3d72 and be7046a.

📒 Files selected for processing (2)
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.test.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.ts

Kitenite added 3 commits March 5, 2026 15:57
Use git rev-parse instead of hardcoding "main" so the test exercises
the guard on environments where git init defaults to "master".
…hGitStatus

After attempting path repair, check existsSync again before calling
getAheadBehindCount. Without this, a stale path silently produces
ahead=0/behind=0 which misleads the UI into showing the workspace
as up-to-date.
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.

1 issue found across 6 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.ts">

<violation number="1" location="apps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.ts:169">
P2: Use `path.basename()` instead of `split("/").pop()` for cross-platform correctness. The manual split won't extract the correct directory name on Windows paths.

(Based on your team's feedback about using cross-platform path utilities instead of split.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

const worktreeName = worktree.path.split("/").pop() ?? worktree.branch;
const worktreePath =
(await resolveWorktreePathWithRepair(worktree.id)) ?? worktree.path;
const worktreeName = worktreePath.split("/").pop() ?? worktree.branch;
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Mar 6, 2026

Choose a reason for hiding this comment

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

P2: Use path.basename() instead of split("/").pop() for cross-platform correctness. The manual split won't extract the correct directory name on Windows paths.

(Based on your team's feedback about using cross-platform path utilities instead of split.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.ts, line 169:

<comment>Use `path.basename()` instead of `split("/").pop()` for cross-platform correctness. The manual split won't extract the correct directory name on Windows paths.

(Based on your team's feedback about using cross-platform path utilities instead of split.) </comment>

<file context>
@@ -164,7 +164,9 @@ export const createGitStatusProcedures = () => {
-				const worktreeName = worktree.path.split("/").pop() ?? worktree.branch;
+				const worktreePath =
+					(await resolveWorktreePathWithRepair(worktree.id)) ?? worktree.path;
+				const worktreeName = worktreePath.split("/").pop() ?? worktree.branch;
 				const branchName = worktree.branch;
 
</file context>
Fix with Cubic

# Conflicts:
#	apps/desktop/src/lib/trpc/routers/workspaces/procedures/query.ts
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