Skip to content

Web/Desktop directory picker persists every subdirectory of $HOME as a project (v2) #46679

Description

@epicwhale

Description

Opening the directory picker in the Web or Desktop app (the "Open folder" / "Add workspace" picker, which starts at $HOME) permanently writes dozens of unrelated directories into the project table. Every ~/.cache, ~/.cargo, ~/.ssh, ~/Downloads, ~/Music and so on then shows up as a project in the TUI's Open dialog (ctrl+o) and in every other project list.

The picker itself is read-only from the user's point of view. Browsing the tree should not register projects.

Expected: Browsing in the picker writes nothing. Only directories the user opens, or that own a session or worktree, appear as projects.

Actual: Every subdirectory of $HOME that the picker preloaded is a row in project and an entry in every project list. Dotdirectories are included.

Reproduced on Linux with the Web app; the TUI shows the result in the Ctrl + O picker.

Plugins

No response

OpenCode version

v2 beta (v0.0.0-beta-18743)

Steps to reproduce

  1. Start the v2 server and open the Web app (or Desktop).
  2. Open the directory picker so it mounts at $HOME. Let the tree render.
  3. Open the TUI and press ctrl+o.
  4. Look at the Projects category, or run:
SELECT id, worktree FROM project WHERE vcs IS NULL ORDER BY time_updated DESC;

If helpful, I've asked Claude Code to write a root cause:

Four steps, all on `v2` at `fd73a85de4`:

1. `packages/app/src/workspaces/selection/dialog.tsx:155` — on first mount the picker preloads every child directory of the start path with `sdk.api.file.list({ location: { directory: absolute } })`, one request per child directory.
2. `packages/protocol/src/api.ts:170` — `FileSystemGroup` runs under `locationMiddleware`, so each of those requests boots a `Location` for the probed directory.
3. `packages/core/src/location.ts:24` — booting a `Location` calls `Project.resolve(ref.directory)`.
4. `packages/core/src/project.ts:357` — a directory without a `.git` or `.hg` marker falls through to `persist({ id: Hash.fast("directory:" + directory), vcs: undefined })`, which is an unconditional `upsertProject`. This branch was added in #45107 ("add directory projects", `9361117504`).

There is no read-only path through `Project.resolve`. The existing `discovery: false` option only skips the config and plugin scan; it does not skip the write.

`Project.list()` (`packages/core/src/project.ts:221`) is a bare `SELECT * FROM project`, and the TUI Open dialog (`packages/tui/src/component/dialog-open.tsx:142`) shows every row except `/` and duplicates. So each probed directory becomes a visible project.

Screenshot and/or share link

No response

Operating System

Linux (ubuntu/debian)

Terminal

NA

Activity

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions