Skip to content

fix(app): scope the tab strip to Code after a side switch - #275

Merged
quickbeard merged 1 commit into
mainfrom
code-strip-scope
Aug 19, 2026
Merged

quickbeard merged 1 commit into
mainfrom
code-strip-scope

Conversation

@quickbeard

Copy link
Copy Markdown
Owner

Switching from Work back to Code left the tab strip stuck on the sandbox folder you had just left: Work's tabs stayed across the top of a Code session, Code's own tabs never appeared, and the + button opened new drafts in the sandbox rather than the project on screen (openNewTab's folder-scope branch fires before every other fallback).

Root cause

Two facts, individually correct, that compose into the bug:

  • useWorkspaceScope's sandbox() memo resolves to nothing in two unrelated states: the selected sandbox's connection is momentarily gone (a restart applying a mount), and the side simply isn't Work anymore.
  • server.retained lists every sandbox that exists, running or not (retainedSandboxServers maps all config.ids) — by design, so tabs survive a restart (fix(app): keep sandbox tabs through a restart, and stop the freeze it caused #247).

The restart hold added in #256 — "keep the previous folder scope while the server it names is retained" — only distinguished the first state. After a Work → Code switch the memo therefore returned the stale Work folder scope on every recomputation, for as long as the sandbox existed. The strip filters tabsStore through that scope, so it showed only the sandbox folder's tabs; every Code tab failed the tab.server !== scope.server test. The sidebar looked fine because home-model.tsx already gates on the side (sideForServerKey(selection().server) !== side()); the strip had no such gate.

Fix

The scope decision moves into the pure nextTabScope in src/workspace/side.ts — the folderEntry pattern from #239, since the Work side cannot be driven from the web e2e harness — and the retained hold is now gated on the side still being Work. useWorkspaceScope becomes a thin reactive wrapper that resolves the sandbox's folders/directory and delegates.

Behaviour after the fix:

Touched files

Fork-only files plus CHANGELOG — no DIVERGENCES.md change needed. The existing row for tabs-outlive-a-restart ("workspace/tabs.tsx keeps the tab strip's folder scope") stays accurate: it keeps it while on Work.

Verification

  • 5 new tests in test-browser/workspace-side.test.ts: the regression (Code side scope wins over a retained previous folder scope), the restart hold (returns the previous scope by reference), the removed-sandbox fall-through, the ordinary folder scope, and the no-folder fallback.
  • bun typecheck clean in packages/app and packages/desktop; test:unit 702 pass, test:browser 106 pass.

Not visually verified — I could not drive the Electron UI here. The repro to check by hand: open a sandbox session on Work, switch to Code, and confirm the strip shows Code's tabs (not the sandbox's), then mount a folder on Work and confirm the strip still holds steady through the restart.

🤖 Generated with Claude Code

Every sandbox that exists sits in server.retained, and useWorkspaceScope's
sandbox() resolves to nothing both while a sandbox restarts and after the
user switches to Code. The restart hold (36411be) only checked the second
half, so switching Work -> Code kept the previous folder scope alive for as
long as the sandbox existed: the strip went on showing Work's tabs over a
Code session, every Code tab was filtered out, and the + button opened new
drafts in the sandbox instead of the project on screen.

The decision moves into the pure nextTabScope in side.ts (the folderEntry
pattern -- Work cannot be driven from the web e2e harness), gated on the
side still being Work before the retained hold applies, with regression
tests either side of the gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@quickbeard
quickbeard merged commit 97c4412 into main Aug 19, 2026
0 of 5 checks passed
@quickbeard
quickbeard deleted the code-strip-scope branch August 19, 2026 10:52
quickbeard added a commit that referenced this pull request Aug 19, 2026
The per-folder tab strips PR #239 gave the Work side now apply to Code:
the strip is keyed by (server, folder) on both sides, where Code's
folders are the selected server's projects. nextTabScope loses its
work-only early return and takes a side-agnostic `selected` server; its
restart hold is re-gated on the previous scope's server belonging to the
side on screen (same fix as #275, now symmetric — a Work switch can no
longer inherit a Code folder scope either). useWorkspaceScope resolves
the selection on both sides, gated on platform.sandboxes so the web
build, which has no Code/Work split, keeps upstream's single strip.

Selecting a Code project in the sidebar becomes a move rather than a
highlight, through the same folderEntry chain Work uses -- last session
in that folder, else its open draft, an open tab, the server's most
recent session there, else a new draft -- and the openSessions step is
no longer gated to Work. The strip's fallback folder becomes per-side
(defaultFolder): the sandbox's primary folder on Work, the last-used
project then the first on Code; the side switch lands through the same
helper so it agrees with the strip. Web keeps the selection toggle.

Co-authored-by: Quickbeard <quickbeard@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant