You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
desktop(v2): selecting Local in New Session shows branch from last selected worktree instead of main worktree #55327
When working with git worktrees in OpenCode Desktop v2, selecting Local (the main worktree) on the New Session screen displays the branch from the last selected/active worktree instead of the main worktree's branch.
PR #49668 updated packages/app/src/new-session/composer-adapter.ts so worktree === "main" (Local) resolves to project.canonical (the main worktree), but packages/app/src/new-session/workspace/controller.ts still resolves and syncs the displayed branch for "main" against sdk().directory (the draft's inherited worktree directory) instead of projectRoot():
packages/app/src/new-session/workspace/controller.ts (branch memo, lines 215–222) passes directory: sdk().directory instead of projectRoot() when value() is "main".
The VCS sync effect (lines 195–201) only syncs sdk().directory and does not sync projectRoot() when projectRoot() !== sdk().directory.
Plugins
None
OpenCode version
OpenCode Desktop 2.0.28
Steps to reproduce
Open a project that has a main checkout (e.g. on main) and a secondary worktree on another branch (e.g. feature/foo).
Select/open a session in the secondary worktree (feature/foo).
Open a New Session tab (+ / Cmd+T) and select Local in the workspace picker.
Observe the branch pill next to Local: it shows feature/foo (from the last selected worktree) instead of main (the main worktree's branch).
Thanks for the detailed report and the pointers to the code. I could confirm this at the code level; there is no desktop app in this sandbox, so I couldn't watch the branch pill itself.
What I did:
Set up a repo with the main checkout on dev and a second worktree on feature.
Called resolveNewSessionBranch from packages/app/src/new-session/workspace/controller.ts the same way the New Session controller does when the draft came from the feature worktree and Local is selected (worktree: "main", directory: sdk().directory).
It returns feature instead of dev, both in 2.0.28 and on the latest dev (165cd2e). As you pointed out:
The branch memo uses sdk().directory instead of the main checkout.
The selection sync effect skips "main".
The composer already sends Local to project.canonical, so the label can disagree with where the session actually starts.
The existing unit test in controller.test.ts also asserts this behaviour (main in /project/feature → feature), so it will need updating along with the fix. The script below reproduces it.
Reproduction script (repro.sh)
#!/usr/bin/env bash# Reproduces #55327 at the logic level: the New Session "Local" branch label is computed by# resolveNewSessionBranch() in packages/app/src/new-session/workspace/controller.ts, which the# controller calls with directory = sdk().directory (the draft's inherited worktree).# Usage: REPO=/path/to/opencode/checkout (deps installed) [REF=v2.0.28] ./repro.shset -euo pipefail
REPO=${REPO:-/workspace/repo}
REF=${REF:-}
WORK=$(mktemp -d)# 1. Real git project: main checkout on "dev", secondary worktree on "feature".
git init -q -b dev "$WORK/project"
git -C "$WORK/project" -c user.email=a@b -c user.name=a commit -q --allow-empty -m init
git -C "$WORK/project" worktree add -q -b feature "$WORK/project-feature"# 2. Load controller.ts from the requested ref (or the working tree).
SRC="$REPO/packages/app/src/new-session/workspace/controller.ts"if [ -n"$REF" ];then
SRC="$REPO/packages/app/src/new-session/workspace/.controller-$REF.ts"
git -C "$REPO" show "$REF:packages/app/src/new-session/workspace/controller.ts">"$SRC"fi
TEST="$REPO/packages/app/src/new-session/workspace/repro-55327.test.ts"
cat >"$TEST"<<EOFimport { test, expect } from "bun:test"import { execSync } from "node:child_process"import { resolveNewSessionBranch } from "$SRC"const main = "$WORK/project", feature = "$WORK/project-feature"const gitBranch = (dir: string) => execSync("git branch --show-current", { cwd: dir }).toString().trim()test("Local shows the main worktree branch", () => { // Same arguments the controller's branch memo passes when the New Session draft was // opened from a session in the secondary worktree (sdk().directory === feature) and Local is selected. const label = resolveNewSessionBranch({ worktree: "main", directory: feature, worktreeBranch: gitBranch }) console.log("main checkout branch:", gitBranch(main), "| label shown for Local:", label) expect(label).toBe(gitBranch(main))})EOFcd"$REPO/packages/app"
bun test --conditions=solid --isolate --preload ./happydom.ts "$TEST"|| STATUS=$?
rm -f "$TEST"${REF:+"$SRC"}exit${STATUS:-0}
Activity
opencode-agent commentedon Oct 14, 2026
Thanks for the detailed report and the pointers to the code. I could confirm this at the code level; there is no desktop app in this sandbox, so I couldn't watch the branch pill itself.
What I did:
devand a second worktree onfeature.resolveNewSessionBranchfrompackages/app/src/new-session/workspace/controller.tsthe same way the New Session controller does when the draft came from thefeatureworktree and Local is selected (worktree: "main",directory: sdk().directory).It returns
featureinstead ofdev, both in 2.0.28 and on the latestdev(165cd2e). As you pointed out:branchmemo usessdk().directoryinstead of the main checkout."main".project.canonical, so the label can disagree with where the session actually starts.The existing unit test in
controller.test.tsalso asserts this behaviour (mainin/project/feature→feature), so it will need updating along with the fix. The script below reproduces it.Reproduction script (repro.sh)
Run it:
bash repro.sh