From 8ebb63483fa27edb32c7c3ae3270e3dc1a1bf590 Mon Sep 17 00:00:00 2001 From: agusmdev Date: Thu, 25 Jun 2026 11:55:32 -0300 Subject: [PATCH] Retry branch auto-rename when metadata is pending --- .../first-work-branch-rename.test.ts | 17 +++++++++++++++++ .../agent-hooks/first-work-branch-rename.ts | 16 +++++++++++++++- src/main/index.ts | 10 ++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/main/agent-hooks/first-work-branch-rename.test.ts b/src/main/agent-hooks/first-work-branch-rename.test.ts index 712d653418..c9a2180883 100644 --- a/src/main/agent-hooks/first-work-branch-rename.test.ts +++ b/src/main/agent-hooks/first-work-branch-rename.test.ts @@ -45,6 +45,7 @@ import { FIRST_WORK_BRANCH_RENAME_SETTLED_CACHE_LIMIT, maybeAutoRenameBranchOnFirstWork, resetFirstWorkBranchRenameState, + type AutoRenameBranchEligibility, type FirstWorkBranchRenameDeps } from './first-work-branch-rename' import { @@ -378,6 +379,22 @@ describe('maybeAutoRenameBranchOnFirstWork', () => { expect(onRenamed).not.toHaveBeenCalled() }) + it('retries when Orca-created eligibility is not available on the first working event', async () => { + let eligibility: AutoRenameBranchEligibility = 'transient-unknown' + const { deps, onRenamed } = makeDeps({ + getAutoRenameBranchEligibility: () => eligibility + }) + + await maybeAutoRenameBranchOnFirstWork(workingEvent(), deps) + expect(generateBranchNameMock).not.toHaveBeenCalled() + + eligibility = 'eligible' + await maybeAutoRenameBranchOnFirstWork(workingEvent(), deps) + + expect(generateBranchNameMock).toHaveBeenCalled() + expect(onRenamed).toHaveBeenCalledWith(REPO_ID) + }) + it('suffixes the branch, display name, and folder together on collision', async () => { gitExecFileAsyncMock.mockImplementation( gitResponder({ diff --git a/src/main/agent-hooks/first-work-branch-rename.ts b/src/main/agent-hooks/first-work-branch-rename.ts index d9155f15d9..51c9086d0c 100644 --- a/src/main/agent-hooks/first-work-branch-rename.ts +++ b/src/main/agent-hooks/first-work-branch-rename.ts @@ -42,6 +42,8 @@ export type FirstWorkBranchRenameEvent = { isReplay: boolean | undefined } +export type AutoRenameBranchEligibility = 'eligible' | 'transient-unknown' | 'permanent-ineligible' + export type FirstWorkBranchRenameDeps = { getSettings: () => GlobalSettings getRepo: (repoId: string) => Repo | undefined @@ -54,6 +56,11 @@ export type FirstWorkBranchRenameDeps = { isPendingFirstAgentMessageRename?: (worktreeId: string) => boolean /** True only for Orca-created worktrees whose branch Orca is allowed to rename. */ canRenameOrcaCreatedBranch: (worktreeId: string) => boolean + /** + * Classifies Orca-created branch metadata. Missing metadata can be transient + * during worktree creation, so it must not permanently settle auto-rename. + */ + getAutoRenameBranchEligibility?: (worktreeId: string) => AutoRenameBranchEligibility /** Persist a new sidebar display name for the worktree. */ setDisplayName: (worktreeId: string, displayName: string) => void /** Align the on-disk folder with the new branch leaf (best-effort, local-only). */ @@ -199,7 +206,14 @@ async function runAutoRename( if (!currentBranch || currentBranch === 'HEAD') { return retry(`no checked-out branch (${currentBranch || 'empty'})`) } - if (!deps.canRenameOrcaCreatedBranch(worktreeId)) { + + const eligibility = + deps.getAutoRenameBranchEligibility?.(worktreeId) ?? + (deps.canRenameOrcaCreatedBranch(worktreeId) ? 'eligible' : 'permanent-ineligible') + if (eligibility === 'transient-unknown') { + return retry('worktree auto-rename eligibility is not available yet') + } + if (eligibility === 'permanent-ineligible') { return stop(`worktree is not eligible for auto-rename`, true) } // Only rename a branch Orca auto-named — never one the user chose. diff --git a/src/main/index.ts b/src/main/index.ts index f879520bed..8799b6e52b 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -255,6 +255,16 @@ function maybeAutoRenameBranchOnFirstWorkFromHook(event: { // Only worktrees Orca stamped at creation are safe to auto-rename. return !!meta?.orcaCreationSource && meta.preserveBranchOnDelete !== true }, + getAutoRenameBranchEligibility: (worktreeId) => { + const meta = currentStore.getWorktreeMeta(worktreeId) + if (!meta) { + return 'transient-unknown' + } + if (!meta.orcaCreationSource || meta.preserveBranchOnDelete === true) { + return 'permanent-ineligible' + } + return 'eligible' + }, setDisplayName: (worktreeId, displayName) => { const scope = parseWorkspaceKey(worktreeId) if (scope?.type === 'folder') {