Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/renderer/src/lib/director-worktree-shell.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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 () => {
Expand Down
29 changes: 5 additions & 24 deletions src/renderer/src/lib/director-worktree-shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
7 changes: 4 additions & 3 deletions src/renderer/src/lib/orchestrator-launch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
})
Expand Down
8 changes: 4 additions & 4 deletions src/renderer/src/lib/orchestrator-launch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down