From d1702e8a40210f452ef41fb64f470b00eacf4fa6 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Tue, 14 Apr 2026 19:38:47 +0700 Subject: [PATCH] fix: defer worktree git watch subscription until after worktree exists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit registerPendingWorktreeWorkspace was subscribing the WorkspaceGitService before the worktree directory existed on disk, caching a stale isGit:false snapshot. When background reconciliation ran, it consumed the stale cache, overwrote the correct workspace record with a wrong projectId, and briefly showed the worktree as a standalone project in the sidebar. Move syncWorkspaceGitWatchTarget from registerPendingWorktreeWorkspace into handleCreatePaseoWorktreeRequest, called after createAgentWorktree, so the first snapshot sees the real worktree — one subscribe, one load, no stale cache to race against. --- packages/server/src/server/session.ts | 4 ++-- packages/server/src/server/worktree-session.ts | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/server/src/server/session.ts b/packages/server/src/server/session.ts index 2a80b0e2d..0b0906f33 100644 --- a/packages/server/src/server/session.ts +++ b/packages/server/src/server/session.ts @@ -5880,8 +5880,6 @@ export class Session { buildPersistedWorkspaceRecord: (input) => this.buildPersistedWorkspaceRecord(input), buildProjectPlacement: (cwd) => this.buildProjectPlacement(cwd), projectRegistry: this.projectRegistry, - syncWorkspaceGitWatchTarget: (cwd, syncOptions) => - this.syncWorkspaceGitWatchTarget(cwd, syncOptions), workspaceRegistry: this.workspaceRegistry, archiveProjectRecordIfEmpty: (projectId, archivedAt) => this.archiveProjectRecordIfEmpty(projectId, archivedAt), @@ -6269,6 +6267,8 @@ export class Session { emit: (message) => this.emit(message), registerPendingWorktreeWorkspace: (options) => this.registerPendingWorktreeWorkspace(options), + syncWorkspaceGitWatchTarget: (cwd, syncOptions) => + this.syncWorkspaceGitWatchTarget(cwd, syncOptions), sessionLogger: this.sessionLogger, runWorktreeSetupInBackground: (options) => this.runWorktreeSetupInBackground(options), }, diff --git a/packages/server/src/server/worktree-session.ts b/packages/server/src/server/worktree-session.ts index 2b343e6c3..eae15c417 100644 --- a/packages/server/src/server/worktree-session.ts +++ b/packages/server/src/server/worktree-session.ts @@ -87,7 +87,6 @@ type RegisterPendingWorktreeWorkspaceDependencies = { }) => PersistedWorkspaceRecord; buildProjectPlacement: (cwd: string) => Promise; projectRegistry: Pick; - syncWorkspaceGitWatchTarget: (cwd: string, options: { isGit: boolean }) => Promise; workspaceRegistry: Pick; archiveProjectRecordIfEmpty: (projectId: string, archivedAt: string) => Promise; }; @@ -111,6 +110,7 @@ type HandleCreatePaseoWorktreeRequestDependencies = { worktreePath: string; branchName: string; }) => Promise; + syncWorkspaceGitWatchTarget: (cwd: string, options: { isGit: boolean }) => Promise; sessionLogger: Logger; runWorktreeSetupInBackground: (options: { requestCwd: string; @@ -535,9 +535,6 @@ export async function registerPendingWorktreeWorkspace( await dependencies.projectRegistry.upsert(nextProjectRecord); await dependencies.workspaceRegistry.upsert(nextWorkspaceRecord); - await dependencies.syncWorkspaceGitWatchTarget(workspaceId, { - isGit: placement.checkout.isGit, - }); if ( existingWorkspace && @@ -593,6 +590,8 @@ export async function handleCreatePaseoWorktreeRequest( paseoHome: dependencies.paseoHome, }); + await dependencies.syncWorkspaceGitWatchTarget(worktreePath, { isGit: true }); + const descriptor = await dependencies.describeWorkspaceRecord(workspace); dependencies.emit({ type: "create_paseo_worktree_response",