diff --git a/src/renderer/src/lib/director-worktree-shell.test.ts b/src/renderer/src/lib/director-worktree-shell.test.ts index 4b34154e63c..d7c66836d8b 100644 --- a/src/renderer/src/lib/director-worktree-shell.test.ts +++ b/src/renderer/src/lib/director-worktree-shell.test.ts @@ -23,6 +23,9 @@ import { createDirectorWorktreeShell } from './director-worktree-shell' // makes activation relaunch an agent — sits well after displayName. const CREATED_WITH_AGENT_ARG_INDEX = 10 const SETUP_DECISION_ARG_INDEX = 3 +// The trailing `options` positional (gates host-side activate) sits last in the +// createWorktree signature, after compareBaseRef. +const CREATE_OPTIONS_ARG_INDEX = 25 const PROJECT: Project = { id: 'proj_1', @@ -49,9 +52,10 @@ describe('createDirectorWorktreeShell', () => { // ...and CRUCIALLY no createdWithAgent — otherwise activation would relaunch an // LLM in the director pane, breaking the token-free invariant. expect(args[CREATED_WITH_AGENT_ARG_INDEX]).toBeUndefined() - // ...and the trailing options suppress host activation so an SSH create - // reveals the director without switching the user's active tab to it. - expect(args.at(-1)).toEqual({ suppressActivation: true }) + // ...and NO trailing suppressActivation option: opening a new Orcastrator is a + // deliberate user action, so an SSH/remote create must activate (focus) it. + const trailingOptions = args[CREATE_OPTIONS_ARG_INDEX] + expect(trailingOptions?.suppressActivation).toBeUndefined() }) it('returns null and toasts when the project has no repo', async () => { diff --git a/src/renderer/src/lib/director-worktree-shell.ts b/src/renderer/src/lib/director-worktree-shell.ts index 6016a01bf3f..d0db7c8091f 100644 --- a/src/renderer/src/lib/director-worktree-shell.ts +++ b/src/renderer/src/lib/director-worktree-shell.ts @@ -54,30 +54,11 @@ export async function createDirectorWorktreeShell( 'skip', undefined, undefined, - `${ORCASTRATOR_DISPLAY_PREFIX}${options.label}`, - // Why: positional args up to compareBaseRef are unused for a director - // shell; the trailing options carries suppressActivation so a remote - // (SSH) create reveals the worktree without switching the user's active - // tab — matching the renderer-side activation suppression at the call site. - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - { suppressActivation: true } + // Why: a new Orcastrator is opened by a deliberate user action, so the host + // create must activate — we pass no trailing suppressActivation option, so an + // SSH/remote create focuses the new director, matching the renderer-side + // activation at the call site. + `${ORCASTRATOR_DISPLAY_PREFIX}${options.label}` ) return { worktreeId: result.worktree.id, setup: result.setup } } catch (error) { diff --git a/src/renderer/src/lib/orchestrator-launch.test.ts b/src/renderer/src/lib/orchestrator-launch.test.ts index f2b55b66481..320678d0bf3 100644 --- a/src/renderer/src/lib/orchestrator-launch.test.ts +++ b/src/renderer/src/lib/orchestrator-launch.test.ts @@ -71,15 +71,16 @@ beforeEach(() => { }) describe('launchOrchestratorForProject', () => { - it('reveals the director worktree but suppresses the active-tab switch', async () => { + it('activates the new Orcastrator (a deliberate user action takes focus)', async () => { const ok = await launchOrchestratorForProject(PROJECT) expect(ok).toBe(true) expect(harness.activate).toHaveBeenCalledTimes(1) const [worktreeId, opts] = harness.activate.mock.calls[0] expect(worktreeId).toBe('wt_director') - // The programmatic launch must NOT yank the user's active tab. - expect(opts.suppressActivation).toBe(true) + // Opening a new Orcastrator is manual and intentional — it must take focus, + // unlike a programmatic worker spawn (which still suppresses activation). + expect(opts.suppressActivation).toBeFalsy() // ...while still revealing it in the sidebar tree + Mission Control DAG. expect(opts.sidebarRevealBehavior).toBe('auto') }) diff --git a/src/renderer/src/lib/orchestrator-launch.ts b/src/renderer/src/lib/orchestrator-launch.ts index 0c501658569..a53142eea9f 100644 --- a/src/renderer/src/lib/orchestrator-launch.ts +++ b/src/renderer/src/lib/orchestrator-launch.ts @@ -74,13 +74,13 @@ export async function launchOrchestratorForProject( allowEmptyPromptLaunch: true }) - // Why: a director is launched programmatically — reveal it in the sidebar + - // Mission Control DAG, but suppress the active-tab switch so the user is not - // yanked off their current worktree when the Orcastrator boots. + // Why: opening a new Orcastrator is a deliberate, manual user action (clicking + // "+" in the ORCASTRATORS section), so it SHOULD take focus and become the + // active tab — exactly like the "+" for a regular worktree. (A programmatic + // worker spawn is the opposite case and still suppresses activation.) const activation = activateAndRevealWorktree(worktreeId, { sidebarRevealBehavior: 'auto', setup, - suppressActivation: true, ...buildDirectWorkItemStartupOpts(agent, startupPlan, 'sidebar') }) if (!activation) {