Skip to content

fix(desktop): handle moved projects and deleted paths - #34137

Closed
opencode-agent[bot] wants to merge 1 commit into
devfrom
desktop-relocated
Closed

fix(desktop): handle moved projects and deleted paths#34137
opencode-agent[bot] wants to merge 1 commit into
devfrom
desktop-relocated

Conversation

@opencode-agent

@opencode-agent opencode-agent Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • omit projects whose persisted primary checkout no longer exists, so Desktop cannot redirect a newly opened path back to the missing one
  • when the same stable project ID is opened from a new path, promote that path, migrate root and nested session directories, reset their context epochs, and remove the stale project-directory record
  • render the filtered Changes tree entirely from Git diff paths without listing synthetic parent directories that may have been deleted

Fixes #30697
Fixes #31869
Related: #25625
Context: #34073

Reproduction evidence

Pinned unmodified base: cd56c51e2d1fabe59e88d7128bb24e546a452053

Moved project

Against the base, using an isolated real opencode serve process and the real app in headless Chromium:

  1. Created and opened Git project project-a.
  2. Created sessions at the project root and project-a/src.
  3. Renamed the directory on disk to project-b.
  4. Opened project-b in the app without pre-registering it.

Observed:

  • project.current(directory=project-b) returned worktree: project-a and sandboxes: [project-b].
  • session.list(directory=project-b) returned []; SQLite retained both session directories under project-a.
  • The browser then issued /path, /session, /vcs, etc. requests for the deleted project-a path.

This follows directly from base fromDirectory: a stable project ID reuses existing.worktree, then classifies the newly opened path as a sandbox. Desktop subsequently canonicalizes sandbox entries to that stale root in layout.tsx.

During verification, the first revision still had a first-open race: the global project.list request could return the missing primary while the new directory was initializing, and the app still requested the old path. The current revision therefore omits unavailable primary checkouts from Project.list. The identical first-readd browser flow now makes zero requests to project-a; the DB ends with worktree: project-b, no sandbox alias, and the root session at project-b.

Deleted path in Changes tree

Against the same base and real app:

  1. Committed src/deleted-parent/file.ts.
  2. Deleted the entire src/deleted-parent directory.
  3. Opened a real session, selected Git changes, and enabled the File tree setting.

The Git diff contained one deleted file. The real browser requested:

  • GET /file?path=src → 200
  • GET /file?path=src/deleted-parent → 500
  • GET /file?path= → 200

The UI displayed Failed to list files / Unexpected server error. The server logged PlatformError: NotFound: FileSystem.realPath (.../src/deleted-parent).

The filtered tree already synthesizes every parent and file from allowed diff paths in file-tree.tsx, but base auto-expands those synthetic directories through file.tree.expand, which lists the filesystem (lines 305-321). FileSystem.list resolves the missing path with realPath(...).pipe(Effect.orDie), producing the 500 (filesystem.ts).

The current revision marks synthetic nodes expanded without loading them and skips root listing for filtered trees. Under the identical real-browser flow, the deleted tree still renders, there are zero /file requests, no toast, and no deleted-path server error. Normal unfiltered file browsing still lists real directories and preserves real errors.

Test plan

  • cd packages/app && bun run test:unit — 433 pass
  • cd packages/app && bun typecheck
  • cd packages/opencode && bun test test/project/project.test.ts — 38 pass
  • cd packages/opencode && bun test --timeout 30000 test/server/httpapi-file.test.ts — 2 pass
  • cd packages/opencode && bun typecheck
  • pre-push monorepo typecheck — 29 packages pass
  • identical isolated before/after opencode serve API flows
  • identical before/after real-app headless Chromium flows

@github-actions

Copy link
Copy Markdown
Contributor

Automated PR Cleanup

Thank you for contributing to opencode.

Due to the high volume of PRs from users and AI agents, we periodically close older PRs using automated criteria so maintainers can focus review time on the most active and community-supported contributions.

This PR was closed because it matched the following cleanup criteria:

  • The PR was created more than 1 month ago
  • The PR had fewer than 2 positive reactions
  • Positive reactions are counted as thumbs-up, heart, celebration, or rocket reactions on the PR

PRs created within the last month are not affected by this cleanup.

If you believe this PR was closed incorrectly, or if you are still actively working on it, please leave a comment explaining why it should be reopened. A maintainer can review and reopen it if appropriate.

Thanks again for taking the time to contribute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

0 participants