Description
While testing the fix for #14082 (PR: #14287), I found a Web UI bug: when opening a git worktree via the + / “Open project” picker, the worktree directory is persisted as a top-level project entry in the sidebar instead of collapsing under the repo root as a workspace. This persists across reloads.
The backend correctly resolves the worktree to the root project (same project id + worktree), and /project + /global/event include the correct sandboxes mapping, but the UI collapse logic fails when the root directory is also present in sandboxes, creating a self-edge and tripping the cycle guard.
Plugins
None
OpenCode version
1.2.17 (built from branch fix-14082-sidebar-sandbox-sessions, opencode --version shows 0.0.0-fix-14082-sidebar-sandbox-sessions-…)
Steps to reproduce
- Start
opencode web (or opencode serve + connect from the Web UI).
- Open a git repo root directory, e.g.:
- Enable “workspaces” for the project in the sidebar.
- Create a git worktree from the repo, e.g.:
- worktree:
/path/to/_wt/repo-feature-branch
- In the Web UI, click
+ (Open project / picker) and open the worktree directory /path/to/_wt/repo-feature-branch.
- Observe: the sidebar shows separate top-level project entries for both:
/path/to/repo
/path/to/_wt/repo-feature-branch
and the worktree entry has workspaces disabled by default.
- Reload the page: the worktree still shows as a separate project.
Expected
If the worktree directory belongs to an existing project (backend indicates this via project.sandboxes), the UI should collapse it under the root project and show it as a workspace, not a separate top-level project entry.
Actual
Worktree stays pinned as a separate top-level project, even though the server reports it as a sandbox of the root project.
Evidence / debugging notes
Backend correctly reports root + sandboxes.
GET /project/current with header x-opencode-directory: /path/to/_wt/repo-feature-branch returns:
worktree: /path/to/repo
sandboxes includes both /path/to/repo and /path/to/_wt/repo-feature-branch.
GET /project returns a project row for /path/to/repo with:
"sandboxes": [
"/path/to/repo",
"/path/to/_wt/repo-feature-branch",
...
]
/global/event emits project.updated for /path/to/repo with the same sandboxes list (including root + worktrees).
The Web UI collapse logic (in packages/app/src/context/layout.tsx) builds a map sandbox -> project.worktree for all entries in sandboxes. When sandboxes includes the root, it creates a self-edge map[root]=root. Resolving a worktree becomes worktree -> root -> root, hits the cycle guard, and returns the original directory, preventing collapse.
Potential fix
In the UI sandbox→root mapping, skip sandbox === project.worktree (do not create root -> root), so rootFor(worktree) resolves to the root worktree and the existing collapse effect can remove the pinned worktree project entry.
Related
(Additional note: opencode web proxies the UI from app.opencode.ai; the fix is expected to be in packages/app.)
Description
While testing the fix for #14082 (PR: #14287), I found a Web UI bug: when opening a git worktree via the
+/ “Open project” picker, the worktree directory is persisted as a top-level project entry in the sidebar instead of collapsing under the repo root as a workspace. This persists across reloads.The backend correctly resolves the worktree to the root project (same project id + worktree), and
/project+/global/eventinclude the correctsandboxesmapping, but the UI collapse logic fails when the root directory is also present insandboxes, creating a self-edge and tripping the cycle guard.Plugins
None
OpenCode version
1.2.17 (built from branch
fix-14082-sidebar-sandbox-sessions,opencode --versionshows0.0.0-fix-14082-sidebar-sandbox-sessions-…)Steps to reproduce
opencode web(oropencode serve+ connect from the Web UI)./path/to/repo/path/to/_wt/repo-feature-branch+(Open project / picker) and open the worktree directory/path/to/_wt/repo-feature-branch./path/to/repo/path/to/_wt/repo-feature-branchand the worktree entry has workspaces disabled by default.
Expected
If the worktree directory belongs to an existing project (backend indicates this via
project.sandboxes), the UI should collapse it under the root project and show it as a workspace, not a separate top-level project entry.Actual
Worktree stays pinned as a separate top-level project, even though the server reports it as a sandbox of the root project.
Evidence / debugging notes
Backend correctly reports root + sandboxes.
GET /project/currentwith headerx-opencode-directory: /path/to/_wt/repo-feature-branchreturns:worktree: /path/to/reposandboxesincludes both/path/to/repoand/path/to/_wt/repo-feature-branch.GET /projectreturns aprojectrow for/path/to/repowith:/global/eventemitsproject.updatedfor/path/to/repowith the samesandboxeslist (including root + worktrees).The Web UI collapse logic (in
packages/app/src/context/layout.tsx) builds a mapsandbox -> project.worktreefor all entries insandboxes. Whensandboxesincludes the root, it creates a self-edgemap[root]=root. Resolving a worktree becomesworktree -> root -> root, hits the cycle guard, and returns the original directory, preventing collapse.Potential fix
In the UI sandbox→root mapping, skip
sandbox === project.worktree(do not createroot -> root), sorootFor(worktree)resolves to the root worktree and the existing collapse effect can remove the pinned worktree project entry.Related
(Additional note:
opencode webproxies the UI from app.opencode.ai; the fix is expected to be inpackages/app.)