diff --git a/src/renderer/src/hooks/useComposerState-host-context-boundaries.test.ts b/src/renderer/src/hooks/useComposerState-host-context-boundaries.test.ts index 75ce1b86d14..24c0ccb1ce0 100644 --- a/src/renderer/src/hooks/useComposerState-host-context-boundaries.test.ts +++ b/src/renderer/src/hooks/useComposerState-host-context-boundaries.test.ts @@ -98,4 +98,13 @@ describe('useComposerState host-context boundaries', () => { ) expect(submitLookup).toContain('sourceContext: selectedRepoGitHubSourceContext') }) + + it('resolves quick-create base refs through the worktree-create precedence helper', () => { + const section = sourceBetween(HOOK_SOURCE, 'const submitBaseBranch', 'const createDisplayName') + + expect(section).toContain('resolveWorktreeCreateBaseBranch') + expect(section).toContain('explicitBaseBranch: baseBranch') + expect(section).toContain('repoWorktreeBaseRef: selectedRepo.worktreeBaseRef') + expect(section).toContain('getRuntimeRepoBaseRefDefault') + }) }) diff --git a/src/renderer/src/hooks/useComposerState.ts b/src/renderer/src/hooks/useComposerState.ts index e99d314edd8..29c25480bf4 100644 --- a/src/renderer/src/hooks/useComposerState.ts +++ b/src/renderer/src/hooks/useComposerState.ts @@ -26,6 +26,7 @@ import { tuiAgentToAgentKind } from '@/lib/telemetry' import { isGitRepoKind } from '../../../shared/repo-kind' import { callRuntimeRpc, getActiveRuntimeTarget } from '@/runtime/runtime-rpc-client' import { getRuntimeRepoBaseRefDefault } from '@/runtime/runtime-repo-client' +import { resolveWorktreeCreateBaseBranch } from '@/runtime/worktree-create-base' import { buildTaskSourceContextFromRepo, type TaskSourceContext @@ -2655,11 +2656,15 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS workspaceName, preserveWorkspaceNameEdits: branchNameOverridePreservesNameEdits }) - const submitBaseBranch = - selectedRepoIsGit && !baseBranch - ? ((await getRuntimeRepoBaseRefDefault(selectedRepoSettings, repoId).catch(() => null)) - ?.defaultBaseRef ?? undefined) - : baseBranch + const submitBaseBranch = selectedRepoIsGit + ? await resolveWorktreeCreateBaseBranch({ + explicitBaseBranch: baseBranch, + repoWorktreeBaseRef: selectedRepo.worktreeBaseRef, + loadDefaultBaseRef: async () => + (await getRuntimeRepoBaseRefDefault(selectedRepoSettings, repoId).catch(() => null)) + ?.defaultBaseRef + }) + : undefined const createDisplayName = smartGitHubResolution?.displayName ?? (nameIsAutoManaged ? submitTitleName?.displayName : undefined) diff --git a/src/renderer/src/runtime/worktree-create-base.test.ts b/src/renderer/src/runtime/worktree-create-base.test.ts new file mode 100644 index 00000000000..67f74558b3b --- /dev/null +++ b/src/renderer/src/runtime/worktree-create-base.test.ts @@ -0,0 +1,42 @@ +import { describe, expect, it, vi } from 'vitest' +import { resolveWorktreeCreateBaseBranch } from './worktree-create-base' + +describe('resolveWorktreeCreateBaseBranch', () => { + it('uses an explicit Start-from selection before repo defaults', async () => { + const loadDefaultBaseRef = vi.fn().mockResolvedValue('origin/main') + + await expect( + resolveWorktreeCreateBaseBranch({ + explicitBaseBranch: 'origin/feature', + repoWorktreeBaseRef: 'dev', + loadDefaultBaseRef + }) + ).resolves.toBe('origin/feature') + + expect(loadDefaultBaseRef).not.toHaveBeenCalled() + }) + + it('uses the pinned repo worktree base before resolving the git primary', async () => { + const loadDefaultBaseRef = vi.fn().mockResolvedValue('origin/main') + + await expect( + resolveWorktreeCreateBaseBranch({ + explicitBaseBranch: undefined, + repoWorktreeBaseRef: ' dev ', + loadDefaultBaseRef + }) + ).resolves.toBe('dev') + + expect(loadDefaultBaseRef).not.toHaveBeenCalled() + }) + + it('falls back to the git primary when no explicit or pinned base exists', async () => { + await expect( + resolveWorktreeCreateBaseBranch({ + explicitBaseBranch: undefined, + repoWorktreeBaseRef: undefined, + loadDefaultBaseRef: vi.fn().mockResolvedValue('origin/main') + }) + ).resolves.toBe('origin/main') + }) +}) diff --git a/src/renderer/src/runtime/worktree-create-base.ts b/src/renderer/src/runtime/worktree-create-base.ts new file mode 100644 index 00000000000..cb3f2fc62f8 --- /dev/null +++ b/src/renderer/src/runtime/worktree-create-base.ts @@ -0,0 +1,15 @@ +export async function resolveWorktreeCreateBaseBranch(args: { + explicitBaseBranch: string | undefined + repoWorktreeBaseRef: string | undefined + loadDefaultBaseRef: () => Promise +}): Promise { + if (args.explicitBaseBranch) { + return args.explicitBaseBranch + } + const pinnedBaseRef = args.repoWorktreeBaseRef?.trim() + if (pinnedBaseRef) { + return pinnedBaseRef + } + const defaultBaseRef = (await args.loadDefaultBaseRef())?.trim() + return defaultBaseRef || undefined +}