fix(desktop): simplify tracked worktree recovery#2973
Conversation
📝 WalkthroughWalkthroughThe pull request introduces a worktree path repair system that detects when Git worktrees have been moved or are missing on disk, enables resolution through Git repair utilities, and provides user-facing recovery actions in the sidebar and workspace page with command display and clipboard copy functionality. Changes
Sequence DiagramsequenceDiagram
actor User
participant UI as UI Component
participant Hook as Recovery Hook
participant Picker as Directory Picker
participant TRPC as TRPC Server
participant Git as Git
participant DB as Local Database
User->>UI: Click "Recover worktree"
UI->>Hook: recoverTrackedWorktree()
Hook->>Picker: Open directory picker
User->>Picker: Select new worktree path
Picker-->>Hook: selectedPath
Hook->>TRPC: repairTrackedWorktreePath({workspaceId, selectedPath})
TRPC->>DB: getWorkspace(workspaceId)
DB-->>TRPC: workspace (type: "worktree")
TRPC->>Git: git worktree repair <selectedPath>
Git-->>TRPC: success
TRPC->>Git: Detect Git root & branch
Git-->>TRPC: branch info
TRPC->>DB: Update worktrees.path to selectedPath
DB-->>TRPC: success
TRPC-->>Hook: {path, pathChanged: true}
Hook->>Hook: Invalidate workspace caches
Hook-->>UI: Show success toast
UI-->>User: Recovery complete
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
4 issues found across 14 files
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.ts">
<violation number="1" location="apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.ts:134">
P1: The existence fast-path can incorrectly resolve a tracked worktree to the main repository path. Exclude mainRepoPath in this check so stale/invalid paths still go through repair resolution.</violation>
</file>
<file name="apps/desktop/src/renderer/react-query/workspaces/useRecoverTrackedWorktree.ts">
<violation number="1" location="apps/desktop/src/renderer/react-query/workspaces/useRecoverTrackedWorktree.ts:43">
P2: Avoid empty `catch {}` here; keep at least warning-level diagnostics so unexpected failures aren’t silently discarded.
(Based on your team's feedback about avoiding empty catch blocks.) [FEEDBACK_USED]</violation>
</file>
<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:239">
P2: Use a cross-platform basename helper instead of splitting on "/". Windows paths use "\\", so this can return the full path instead of the folder name.
(Based on your team's feedback about using cross-platform path utilities instead of split.) [FEEDBACK_USED]</violation>
</file>
<file name="apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx">
<violation number="1" location="apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx:487">
P2: Handle errors from `recoverTrackedWorktree()` instead of discarding its promise with `void`; otherwise a failed directory-selection mutation can become an unhandled rejection.
(Based on your team's feedback about awaiting/catching async calls to avoid unhandled promise rejections.) [FEEDBACK_USED]</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| }; | ||
| } | ||
|
|
||
| if (existsSync(context.worktree.path)) { |
There was a problem hiding this comment.
P1: The existence fast-path can incorrectly resolve a tracked worktree to the main repository path. Exclude mainRepoPath in this check so stale/invalid paths still go through repair resolution.
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/utils/repair-worktree-path.ts, line 134:
<comment>The existence fast-path can incorrectly resolve a tracked worktree to the main repository path. Exclude mainRepoPath in this check so stale/invalid paths still go through repair resolution.</comment>
<file context>
@@ -0,0 +1,398 @@
+ };
+ }
+
+ if (existsSync(context.worktree.path)) {
+ return {
+ pathChanged: false,
</file context>
| workspaceId, | ||
| selectedPath: result.path, | ||
| }); | ||
| } catch { |
There was a problem hiding this comment.
P2: Avoid empty catch {} here; keep at least warning-level diagnostics so unexpected failures aren’t silently discarded.
(Based on your team's feedback about avoiding empty catch blocks.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/react-query/workspaces/useRecoverTrackedWorktree.ts, line 43:
<comment>Avoid empty `catch {}` here; keep at least warning-level diagnostics so unexpected failures aren’t silently discarded.
(Based on your team's feedback about avoiding empty catch blocks.) </comment>
<file context>
@@ -0,0 +1,52 @@
+ workspaceId,
+ selectedPath: result.path,
+ });
+ } catch {
+ // Mutation onError already surfaces the failure to the user.
+ }
</file context>
|
|
||
| const worktreeName = worktree.path.split("/").pop() ?? worktree.branch; | ||
| const resolvedPath = await resolveWorktreePathWithRepair(worktree.id); | ||
| const worktreeName = resolvedPath?.split("/").pop() ?? worktree.branch; |
There was a problem hiding this comment.
P2: Use a cross-platform basename helper instead of splitting on "/". Windows paths use "\", so this can return the full path instead of the folder name.
(Based on your team's feedback about using cross-platform path utilities instead of split.)
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 239:
<comment>Use a cross-platform basename helper instead of splitting on "/". Windows paths use "\\", so this can return the full path instead of the folder name.
(Based on your team's feedback about using cross-platform path utilities instead of split.) </comment>
<file context>
@@ -217,7 +235,8 @@ export const createGitStatusProcedures = () => {
- const worktreeName = worktree.path.split("/").pop() ?? worktree.branch;
+ const resolvedPath = await resolveWorktreePathWithRepair(worktree.id);
+ const worktreeName = resolvedPath?.split("/").pop() ?? worktree.branch;
const branchName = worktree.branch;
</file context>
| isUnread={isUnread} | ||
| workspaceStatus={workspaceStatus} | ||
| sections={sections} | ||
| onRecoverWorktree={() => void recoverTrackedWorktree()} |
There was a problem hiding this comment.
P2: Handle errors from recoverTrackedWorktree() instead of discarding its promise with void; otherwise a failed directory-selection mutation can become an unhandled rejection.
(Based on your team's feedback about awaiting/catching async calls to avoid unhandled promise rejections.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx, line 487:
<comment>Handle errors from `recoverTrackedWorktree()` instead of discarding its promise with `void`; otherwise a failed directory-selection mutation can become an unhandled rejection.
(Based on your team's feedback about awaiting/catching async calls to avoid unhandled promise rejections.) </comment>
<file context>
@@ -440,9 +480,11 @@ export function WorkspaceListItem({
isUnread={isUnread}
workspaceStatus={workspaceStatus}
sections={sections}
+ onRecoverWorktree={() => void recoverTrackedWorktree()}
onRename={rename.startRename}
onOpenInFinder={handleOpenInFinder}
</file context>
| onRecoverWorktree={() => void recoverTrackedWorktree()} | |
| onRecoverWorktree={() => { | |
| void recoverTrackedWorktree().catch((error) => { | |
| toast.error( | |
| `Failed to recover worktree: ${error instanceof Error ? error.message : String(error)}`, | |
| ); | |
| }); | |
| }} |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/workspace/$workspaceId/page.tsx (1)
128-140:⚠️ Potential issue | 🟠 Major
showRecoveryViewonly blocks the layout, not the stale-path side effects.The new branch still leaves path-dependent hooks and hotkeys mounted above it (
useWorkspaceFileEventBridge,useWorkspaceRenameReconciliation,RUN_WORKSPACE_COMMAND,OPEN_IN_APP,COPY_PATH), all keyed offworkspace.worktreePath. Inrepair_required, that is exactly the stale path the user is being asked to fix, so the fullscreen recovery state can still trigger broken operations. Gate those effects/callbacks behind a singlecanUseWorktreePathflag while recovery is pending.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/workspace/`$workspaceId/page.tsx around lines 128 - 140, The recovery fullscreen (showRecoveryView) blocks rendering but not path-dependent effects, so create a boolean canUseWorktreePath (false when workspace?.type === "worktree" && workspace.repairState === "repair_required") and use it to gate mounting/activation of all worktree-path-dependent hooks and callbacks: wrap calls to useWorkspaceFileEventBridge and useWorkspaceRenameReconciliation and disable handlers that register RUN_WORKSPACE_COMMAND, OPEN_IN_APP, COPY_PATH when canUseWorktreePath is false; ensure showInitView logic (isInitializing/hasFailed/hasIncompleteInit) remains unchanged but these side-effects do not run while repair is pending.
🧹 Nitpick comments (1)
apps/desktop/src/lib/trpc/routers/workspaces/procedures/query.ts (1)
258-285: Avoid per-workspace Git scans ingetAllGrouped.
resolveWorkspacePathState()can shell out throughgetBranchWorktreePath()for every unresolved tracked worktree. Doing that insidePromise.all(allWorkspaces.map(...))means one sidebar refresh can fan out into Ngit worktree listscans for the same project. Caching bymainRepoPathor precomputing the repo's worktree map once would keep recovery-state calculation off the hot path.🤖 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/procedures/query.ts` around lines 258 - 285, The current loop calls resolveWorkspacePathState for every workspace which can invoke getBranchWorktreePath and trigger N git worktree scans; instead, before Promise.all over allWorkspaces (in getAllGrouped), group workspaces by their mainRepoPath (use groupsMap.get(workspace.projectId).project.mainRepoPath), compute a single worktree map per mainRepoPath (via a new helper like getWorktreeMapForRepo or by calling getBranchWorktreePath once and building a map), then pass that cached map into resolveWorkspacePathState (or add an overload resolveWorkspacePathState(workspace, worktreeMap)) so the per-workspace mapping uses the precomputed map rather than shelling out repeatedly. Ensure you reference resolveWorkspacePathState, getBranchWorktreePath, allWorkspaces and groupsMap when modifying the logic.
🤖 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/utils/git.ts`:
- Around line 825-839: repairWorktreeRegistration currently rethrows the raw
simple-git error; change it to normalize failures into a TRPCError so they match
the surrounding recovery flow. Inside repairWorktreeRegistration (and keep the
same getSimpleGitWithShellPath / git.raw call), catch errors and throw a new
TRPCError (imported from '@trpc/server') with an appropriate code (e.g.,
'BAD_REQUEST' or 'NOT_FOUND') and a clear message that includes the original
error.message to preserve details; do not rethrow the raw error so callers like
repairTrackedWorktreePath receive the structured TRPCError shape.
In `@apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.ts`:
- Around line 144-177: The current code treats any non-persist path as
"git_repair_required" even when getBranchWorktreePath() failed to probe Git or
when Git reports the branch is registered at the main repo; change the control
flow to (1) track probe failure by introducing a probeFailed boolean set true
inside the catch for getBranchWorktreePath, (2) only return git_repair_required
and call persistResolvedTrackedWorktreePath when registeredPath is non-null,
existsSync(registeredPath) is true and isMainRepoPath(context, registeredPath)
is false (keep existing check), (3) if probeFailed is true return a resolution
with status "git_probe_failed" (including branch, mainRepoPath, storedPath and
registeredPath=null), and (4) if registeredPath is non-null but
isMainRepoPath(context, registeredPath) is true treat it as not
registered/missing by returning a resolution with status "git_not_registered"
(include branch, mainRepoPath, registeredPath and storedPath). Use the existing
functions getBranchWorktreePath, isMainRepoPath, and
persistResolvedTrackedWorktreePath and the context.worktree fields to implement
these branches.
In
`@apps/desktop/src/renderer/react-query/workspaces/useRecoverTrackedWorktree.ts`:
- Around line 14-15: The directory picker call selectDirectory.mutateAsync() is
executed outside the try/catch in recoverTrackedWorktree, so picker/IPC errors
become unhandled rejections; move or wrap the selectDirectory.mutateAsync() call
inside the existing try/catch (or add a dedicated try/catch around it) in the
recoverTrackedWorktree flow so any rejection is caught, call the same
toast/error handling used for repairTrackedWorktree failures, and ensure the
function returns early on picker failure to avoid continuing with invalid data.
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceContextMenu.tsx`:
- Around line 204-212: When isRepairRequired is true we must not render the
path-based actions from commonContextMenuItems next to the Recover worktree
entry because they operate on a stale path; update WorkspaceContextMenu so that
when isRepairRequired is true it either filters out or disables the path-related
items from commonContextMenuItems (the Open in Finder / Copy Path handlers)
instead of rendering them normally, and ensure onRecoverWorktree updates the
repair state so the items are restored when repair completes; specifically
locate the usage of commonContextMenuItems in WorkspaceContextMenu and change
rendering to conditional rendering or pass a prop/flag to those menu items to
render disabled/no-op handlers while isRepairRequired is true.
---
Outside diff comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/workspace/`$workspaceId/page.tsx:
- Around line 128-140: The recovery fullscreen (showRecoveryView) blocks
rendering but not path-dependent effects, so create a boolean canUseWorktreePath
(false when workspace?.type === "worktree" && workspace.repairState ===
"repair_required") and use it to gate mounting/activation of all
worktree-path-dependent hooks and callbacks: wrap calls to
useWorkspaceFileEventBridge and useWorkspaceRenameReconciliation and disable
handlers that register RUN_WORKSPACE_COMMAND, OPEN_IN_APP, COPY_PATH when
canUseWorktreePath is false; ensure showInitView logic
(isInitializing/hasFailed/hasIncompleteInit) remains unchanged but these
side-effects do not run while repair is pending.
---
Nitpick comments:
In `@apps/desktop/src/lib/trpc/routers/workspaces/procedures/query.ts`:
- Around line 258-285: The current loop calls resolveWorkspacePathState for
every workspace which can invoke getBranchWorktreePath and trigger N git
worktree scans; instead, before Promise.all over allWorkspaces (in
getAllGrouped), group workspaces by their mainRepoPath (use
groupsMap.get(workspace.projectId).project.mainRepoPath), compute a single
worktree map per mainRepoPath (via a new helper like getWorktreeMapForRepo or by
calling getBranchWorktreePath once and building a map), then pass that cached
map into resolveWorkspacePathState (or add an overload
resolveWorkspacePathState(workspace, worktreeMap)) so the per-workspace mapping
uses the precomputed map rather than shelling out repeatedly. Ensure you
reference resolveWorkspacePathState, getBranchWorktreePath, allWorkspaces and
groupsMap when modifying the logic.
🪄 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: 2916f6ea-96d5-40ec-acdc-15f5379b500f
📒 Files selected for processing (14)
apps/desktop/src/lib/trpc/routers/terminal/terminal.tsapps/desktop/src/lib/trpc/routers/workspaces/procedures/git-status.tsapps/desktop/src/lib/trpc/routers/workspaces/procedures/query.tsapps/desktop/src/lib/trpc/routers/workspaces/procedures/repair.tsapps/desktop/src/lib/trpc/routers/workspaces/utils/git.tsapps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.tsapps/desktop/src/lib/trpc/routers/workspaces/workspaces.tsapps/desktop/src/renderer/react-query/workspaces/index.tsapps/desktop/src/renderer/react-query/workspaces/useRecoverTrackedWorktree.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/workspace/$workspaceId/page.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceList/WorkspaceList.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceContextMenu.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/types.ts
| export async function repairWorktreeRegistration({ | ||
| mainRepoPath, | ||
| worktreePath, | ||
| }: { | ||
| mainRepoPath: string; | ||
| worktreePath: string; | ||
| }): Promise<void> { | ||
| try { | ||
| const git = await getSimpleGitWithShellPath(mainRepoPath); | ||
| await git.raw(["worktree", "repair", worktreePath]); | ||
| } catch (error) { | ||
| console.error(`Failed to repair worktree registration: ${error}`); | ||
| throw error; | ||
| } | ||
| } |
There was a problem hiding this comment.
Normalize git worktree repair failures before they escape the recovery flow.
repairTrackedWorktreePath() calls this helper without its own catch, so rethrowing the raw simple-git error here makes this step the only one in the flow that skips the structured TRPCError mapping used for invalid folders, wrong branches, and missing paths. A bad selection at this point will fall through as an untyped backend failure instead of the same actionable recovery error shape as the surrounding checks.
🤖 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/git.ts` around lines 825 -
839, repairWorktreeRegistration currently rethrows the raw simple-git error;
change it to normalize failures into a TRPCError so they match the surrounding
recovery flow. Inside repairWorktreeRegistration (and keep the same
getSimpleGitWithShellPath / git.raw call), catch errors and throw a new
TRPCError (imported from '@trpc/server') with an appropriate code (e.g.,
'BAD_REQUEST' or 'NOT_FOUND') and a clear message that includes the original
error.message to preserve details; do not rethrow the raw error so callers like
repairTrackedWorktreePath receive the structured TRPCError shape.
| let registeredPath: string | null = null; | ||
| try { | ||
| registeredPath = await getBranchWorktreePath({ | ||
| mainRepoPath: context.mainRepoPath, | ||
| branch: context.worktree.branch, | ||
| }); | ||
| } catch (error) { | ||
| console.warn( | ||
| `[repair-worktree-path] Failed to inspect Git worktree state for ${context.worktree.id}:`, | ||
| error instanceof Error ? error.message : error, | ||
| ); | ||
| } | ||
|
|
||
| if ( | ||
| registeredPath && | ||
| existsSync(registeredPath) && | ||
| !isMainRepoPath(context, registeredPath) | ||
| ) { | ||
| return persistResolvedTrackedWorktreePath({ | ||
| context, | ||
| resolvedPath: registeredPath, | ||
| }); | ||
| } | ||
|
|
||
| return { | ||
| pathChanged: false, | ||
| resolution: { | ||
| status: "git_repair_required", | ||
| branch: context.worktree.branch, | ||
| mainRepoPath: context.mainRepoPath, | ||
| registeredPath, | ||
| storedPath: context.worktree.path, | ||
| }, | ||
| }; |
There was a problem hiding this comment.
Differentiate probe failures from genuine repairable moves.
This block falls back to git_repair_required even when getBranchWorktreePath() could not inspect Git at all, or when Git only reports mainRepoPath for the branch. In both cases the sidebar/page recovery flow and resolveWorktreePathOrThrow* callers will tell the user to repair a moved worktree that Git never actually identified. Only emit the repair state when Git positively points at a non-main worktree registration; otherwise surface the probe failure or treat it as missing.
🤖 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.ts`
around lines 144 - 177, The current code treats any non-persist path as
"git_repair_required" even when getBranchWorktreePath() failed to probe Git or
when Git reports the branch is registered at the main repo; change the control
flow to (1) track probe failure by introducing a probeFailed boolean set true
inside the catch for getBranchWorktreePath, (2) only return git_repair_required
and call persistResolvedTrackedWorktreePath when registeredPath is non-null,
existsSync(registeredPath) is true and isMainRepoPath(context, registeredPath)
is false (keep existing check), (3) if probeFailed is true return a resolution
with status "git_probe_failed" (including branch, mainRepoPath, storedPath and
registeredPath=null), and (4) if registeredPath is non-null but
isMainRepoPath(context, registeredPath) is true treat it as not
registered/missing by returning a resolution with status "git_not_registered"
(include branch, mainRepoPath, registeredPath and storedPath). Use the existing
functions getBranchWorktreePath, isMainRepoPath, and
persistResolvedTrackedWorktreePath and the context.worktree fields to implement
these branches.
| const selectDirectory = electronTrpc.window.selectDirectory.useMutation(); | ||
| const repairTrackedWorktree = |
There was a problem hiding this comment.
Handle directory-picker failures too.
selectDirectory.mutateAsync() sits outside the try/catch. Since callers invoke recoverTrackedWorktree() with void, any picker/IPC failure becomes an unhandled rejection and no toast is shown.
Suggested fix
- const selectDirectory = electronTrpc.window.selectDirectory.useMutation();
+ const selectDirectory = electronTrpc.window.selectDirectory.useMutation({
+ onError: (error) => {
+ toast.error(`Failed to choose folder: ${error.message}`);
+ },
+ });
const recoverTrackedWorktree = async () => {
- const result = await selectDirectory.mutateAsync({
- title: "Select moved worktree folder",
- defaultPath: defaultPath ?? undefined,
- });
- if (result.canceled || !result.path) {
- return;
- }
-
try {
+ const result = await selectDirectory.mutateAsync({
+ title: "Select moved worktree folder",
+ defaultPath: defaultPath ?? undefined,
+ });
+ if (result.canceled || !result.path) {
+ return;
+ }
+
await repairTrackedWorktree.mutateAsync({
workspaceId,
selectedPath: result.path,
});
} catch {
- // Mutation onError already surfaces the failure to the user.
+ // Mutation onError handlers already surface failures to the user.
}
};Also applies to: 29-45
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/desktop/src/renderer/react-query/workspaces/useRecoverTrackedWorktree.ts`
around lines 14 - 15, The directory picker call selectDirectory.mutateAsync() is
executed outside the try/catch in recoverTrackedWorktree, so picker/IPC errors
become unhandled rejections; move or wrap the selectDirectory.mutateAsync() call
inside the existing try/catch (or add a dedicated try/catch around it) in the
recoverTrackedWorktree flow so any rejection is caught, call the same
toast/error handling used for repairTrackedWorktree failures, and ensure the
function returns early on picker failure to avoid continuing with invalid data.
| {isRepairRequired && ( | ||
| <> | ||
| <ContextMenuItem onSelect={onRecoverWorktree}> | ||
| <LuWrench className="size-4 mr-2" strokeWidth={STROKE_WIDTH} /> | ||
| Recover worktree | ||
| </ContextMenuItem> | ||
| <ContextMenuSeparator /> | ||
| </> | ||
| )} |
There was a problem hiding this comment.
Don’t keep stale-path actions next to “Recover worktree”.
When isRepairRequired is true, this adds the recovery entry but still renders Open in Finder and Copy Path from commonContextMenuItems immediately below. Those handlers still target the stale tracked path that triggered recovery, so the menu exposes known-broken actions in the very state that is supposed to steer the user toward repair. Hide or disable the path-based entries until recovery succeeds.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceContextMenu.tsx`
around lines 204 - 212, When isRepairRequired is true we must not render the
path-based actions from commonContextMenuItems next to the Recover worktree
entry because they operate on a stale path; update WorkspaceContextMenu so that
when isRepairRequired is true it either filters out or disables the path-related
items from commonContextMenuItems (the Open in Finder / Copy Path handlers)
instead of rendering them normally, and ensure onRecoverWorktree updates the
repair state so the items are restored when repair completes; specifically
locate the usage of commonContextMenuItems in WorkspaceContextMenu and change
rendering to conditional rendering or pass a prop/flag to those menu items to
render disabled/no-op handlers while isRepairRequired is true.
Summary
origin/mainas a compact implementationrepairTrackedWorktreePathmutation plus sidebar/workspace recovery actions that prompt for the moved folderValidation
bun run --filter @superset/desktop typecheckbunx biome check apps/desktop/src/lib/trpc/routers/workspaces/procedures/query.ts 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/procedures/repair.ts apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts apps/desktop/src/lib/trpc/routers/workspaces/utils/repair-worktree-path.ts apps/desktop/src/renderer/react-query/workspaces/useRecoverTrackedWorktree.ts apps/desktop/src/renderer/react-query/workspaces/index.ts apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/types.ts apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceList/WorkspaceList.tsx apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceContextMenu.tsx apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx 'apps/desktop/src/renderer/routes/_authenticated/_dashboard/workspace/$workspaceId/page.tsx'Notes
Summary by cubic
Add an explicit tracked worktree recovery flow that resolves moved Git worktrees, prompts users to repair paths, and prevents terminal/status from using stale paths. This replaces the auto-repair POC with a simple, user-driven flow.
New Features
workspaces.repairTrackedWorktreePathand a compact resolver inrepair-worktree-path(resolveWorktreePath*,repairTrackedWorktreePath) that usesgit worktree repair.repairState,repairMessage,repairCommand, andexistsOnDiskto drive UI.useRecoverTrackedWorktreeto guide path selection.Bug Fixes
worktrees.path; errors include recovery guidance.pathChangedflag.getWorkspaceCwd, PR status/comments, and worktree name resolution now use the resolved path.Written for commit d51a007. Summary will update on new commits.
Summary by CodeRabbit