Skip to content

Session-scoped terminals: switch tabs on worktree/session select#290

Open
kirich1409 wants to merge 9 commits intomainfrom
fix/session-switch-terminal
Open

Session-scoped terminals: switch tabs on worktree/session select#290
kirich1409 wants to merge 9 commits intomainfrom
fix/session-switch-terminal

Conversation

@kirich1409
Copy link
Copy Markdown
Contributor

@kirich1409 kirich1409 commented Apr 24, 2026

Summary

Implements session-scoped terminal navigation and fixes duplicate tab creation.

Problem

  • Selecting a worktree did not switch the active terminal tab
  • Reopening a remote session created a duplicate terminal tab
  • Worktree directories were incorrectly registered as "Recent Projects"

Changes

Model (Tab)

  • Tab.remoteSessionID: String? — links tab to cloud session (dedup key)
  • Tab.worktreeID: UUID? — links tab to local worktree (now populated everywhere)

Reducer (MainFeature)

  • selectWorktree intercept — focuses existing tab for that worktree; if none exists, auto-creates a shell session and tab synchronously
  • Session-scoped tab barselectWorktree now auto-opens a shell when switching to a worktree with no open terminal
  • Dedup for remote sessionsnewRemoteSession(server:session:) looks up existing tab by remoteSessionID before creating a new one
  • Dedup for local sessionsnewClaudeSession looks up existing tab by worktreeID
  • worktreeID populated everywhere — shell tabs, default tab bound to main worktree
  • Project registration guardnewShellSession skips _projectOpened for known worktree paths (isKnownWorktree check), preventing worktrees from appearing in Recent Projects

UI (TabBarView)

  • New sessionFilter: UUID? parameter — shows only tabs for the selected worktree/session
  • AppRootView passes store.worktree.selectedWorktreeID as filter

Test plan

  • SessionSwitchTerminalTests — 10 TCA/unit tests (50 total, all green):
    • Remote: open session → tab with remoteSessionID; reopen → focus existing, no duplicate
    • Remote: two different sessions → two separate tabs; switch back → focus correct tab
    • Local: selectWorktree → focus existing tab; auto-create shell when no tab
    • Guard: isKnownWorktree true for worktree paths; false for unknown paths
  • xcodebuild test — 50/50 passed
  • swiftlint lint --strict — 0 violations

🤖 Generated with Claude Code

kirich1409 and others added 7 commits April 24, 2026 22:58
…ting

- MainFeature.Action.newRemoteSession gains optional session: RemoteSession? parameter
- Reducer looks up tabs by remoteSessionID before creating a new one; if found, focuses it
- MainFeature.State.init respects a non-empty tabs argument instead of always creating default tab
- AppFeature.openRemoteSession passes the session through to MainFeature
- TerminalFeature.start adds a catch handler so gRPC connection errors update connectionState
  instead of leaking as unhandled throws (fixes test isolation)
- SessionSwitchTerminalTests: all 6 tests now pass
newClaudeSession now checks for an existing tab with matching
worktreeID before creating a new one. New tabs created via
orchestrator(.newSession) receive worktreeID from the current
worktree selection so future lookups can find them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MainFeature now intercepts selectWorktree action and focuses
the tab bound to the selected worktree (worktreeID match).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shell tabs (newShellTab, newShellSession) now receive worktreeID
from selectedWorktreeID. Default tab is bound to the main worktree
when its path matches the project root — both eagerly (on shell
session start) and lazily (on selectWorktree with path fallback).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
selectWorktree now auto-opens a shell in the worktree directory
when no existing tab is found, instead of silently doing nothing.
This gives every worktree its own terminal on first selection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
selectWorktree Fallback B now creates the tab synchronously before
sending the shell session effect — no race between effect dispatch
and test assertion. orchestrator(.newShellSession) skips tab creation
when a tab for the current worktree already exists.

New tests: selectWorktreeFocusesExistingTab,
selectWorktreeAutoCreatesShell. All 48 tests green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TabBarView now accepts sessionFilter: UUID? and shows only tabs
whose worktreeID matches the selected worktree. AppRootView passes
worktree.selectedWorktreeID as the filter, so each worktree/session
sees only its own set of terminals.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@kirich1409 kirich1409 changed the title Focus existing tab when reopening remote session Session-scoped terminal tabs (worktree switching) Apr 25, 2026
kirich1409 and others added 2 commits April 25, 2026 08:00
newShellSession no longer calls _projectOpened when workingDirectory
is inside the current project (worktree path). Fixes bug where every
worktree selection added the worktree as a Recent Project entry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Restore currentProject == nil guard in orchestrator(.newSession)
  (sed replacement was too broad and broke the Claude session path)
- Add two unit tests for the isKnownWorktree guard logic:
  known worktree path → guard true → registration skipped
  unknown path → guard false → registration allowed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@kirich1409 kirich1409 changed the title Session-scoped terminal tabs (worktree switching) Session-scoped terminals: switch tabs on worktree/session select Apr 25, 2026
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