fix(server): await git worktree creation before responding to client

Move createAgentWorktree() from the fire-and-forget background function
into handleCreatePaseoWorktreeRequest so the worktree directory exists
on disk before the client receives the response and navigates. Fixes
ENOENT race when the app queries checkout status on a path that hasn't
been created yet. Setup commands (npm install, etc.) remain background.
This commit is contained in:
Mohamed Boudra
2026-04-13 14:55:59 +07:00
parent b3d5401295
commit 8aa1833e2b
2 changed files with 17 additions and 20 deletions
+4 -5
View File
@@ -173,7 +173,7 @@ import { ScheduleService } from "./schedule/service.js";
import { import {
assertSafeGitRef as assertWorktreeSafeGitRef, assertSafeGitRef as assertWorktreeSafeGitRef,
buildAgentSessionConfig as buildWorktreeAgentSessionConfig, buildAgentSessionConfig as buildWorktreeAgentSessionConfig,
createPaseoWorktreeInBackground as createWorktreeInBackgroundSession, runWorktreeSetupInBackground as runWorktreeSetupInBackgroundSession,
handleCreatePaseoWorktreeRequest as handleCreateWorktreeRequest, handleCreatePaseoWorktreeRequest as handleCreateWorktreeRequest,
handlePaseoWorktreeArchiveRequest as handleWorktreeArchiveRequest, handlePaseoWorktreeArchiveRequest as handleWorktreeArchiveRequest,
handlePaseoWorktreeListRequest as handleWorktreeListRequest, handlePaseoWorktreeListRequest as handleWorktreeListRequest,
@@ -6157,20 +6157,19 @@ export class Session {
registerPendingWorktreeWorkspace: (options) => registerPendingWorktreeWorkspace: (options) =>
this.registerPendingWorktreeWorkspace(options), this.registerPendingWorktreeWorkspace(options),
sessionLogger: this.sessionLogger, sessionLogger: this.sessionLogger,
createPaseoWorktreeInBackground: (options) => this.createPaseoWorktreeInBackground(options), runWorktreeSetupInBackground: (options) => this.runWorktreeSetupInBackground(options),
}, },
request, request,
); );
} }
private async createPaseoWorktreeInBackground(options: { private async runWorktreeSetupInBackground(options: {
requestCwd: string; requestCwd: string;
repoRoot: string; repoRoot: string;
baseBranch: string;
slug: string; slug: string;
worktreePath: string; worktreePath: string;
}): Promise<void> { }): Promise<void> {
return createWorktreeInBackgroundSession( return runWorktreeSetupInBackgroundSession(
{ {
paseoHome: this.paseoHome, paseoHome: this.paseoHome,
emitWorkspaceUpdateForCwd: (cwd) => this.emitWorkspaceUpdateForCwd(cwd), emitWorkspaceUpdateForCwd: (cwd) => this.emitWorkspaceUpdateForCwd(cwd),
+13 -15
View File
@@ -109,10 +109,9 @@ type HandleCreatePaseoWorktreeRequestDependencies = {
branchName: string; branchName: string;
}) => Promise<PersistedWorkspaceRecord>; }) => Promise<PersistedWorkspaceRecord>;
sessionLogger: Logger; sessionLogger: Logger;
createPaseoWorktreeInBackground: (options: { runWorktreeSetupInBackground: (options: {
requestCwd: string; requestCwd: string;
repoRoot: string; repoRoot: string;
baseBranch: string;
slug: string; slug: string;
worktreePath: string; worktreePath: string;
}) => Promise<void>; }) => Promise<void>;
@@ -575,6 +574,15 @@ export async function handleCreatePaseoWorktreeRequest(
worktreePath, worktreePath,
branchName: normalizedSlug, branchName: normalizedSlug,
}); });
await createAgentWorktree({
cwd: repoRoot,
branchName: normalizedSlug,
baseBranch,
worktreeSlug: normalizedSlug,
paseoHome: dependencies.paseoHome,
});
const descriptor = await dependencies.describeWorkspaceRecord(workspace); const descriptor = await dependencies.describeWorkspaceRecord(workspace);
dependencies.emit({ dependencies.emit({
type: "create_paseo_worktree_response", type: "create_paseo_worktree_response",
@@ -586,10 +594,9 @@ export async function handleCreatePaseoWorktreeRequest(
}, },
}); });
void dependencies.createPaseoWorktreeInBackground({ void dependencies.runWorktreeSetupInBackground({
requestCwd: request.cwd, requestCwd: request.cwd,
repoRoot, repoRoot,
baseBranch,
slug: normalizedSlug, slug: normalizedSlug,
worktreePath, worktreePath,
}); });
@@ -611,12 +618,11 @@ export async function handleCreatePaseoWorktreeRequest(
} }
} }
export async function createPaseoWorktreeInBackground( export async function runWorktreeSetupInBackground(
dependencies: CreatePaseoWorktreeInBackgroundDependencies, dependencies: CreatePaseoWorktreeInBackgroundDependencies,
options: { options: {
requestCwd: string; requestCwd: string;
repoRoot: string; repoRoot: string;
baseBranch: string;
slug: string; slug: string;
worktreePath: string; worktreePath: string;
}, },
@@ -624,14 +630,6 @@ export async function createPaseoWorktreeInBackground(
let setupTerminalId: string | null = null; let setupTerminalId: string | null = null;
try { try {
await createAgentWorktree({
cwd: options.repoRoot,
branchName: options.slug,
baseBranch: options.baseBranch,
worktreeSlug: options.slug,
paseoHome: dependencies.paseoHome,
});
const setupCommands = getWorktreeSetupCommands(options.worktreePath); const setupCommands = getWorktreeSetupCommands(options.worktreePath);
if (setupCommands.length > 0 && dependencies.terminalManager) { if (setupCommands.length > 0 && dependencies.terminalManager) {
const runtimeEnv = await resolveWorktreeRuntimeEnv({ const runtimeEnv = await resolveWorktreeRuntimeEnv({
@@ -667,7 +665,7 @@ export async function createPaseoWorktreeInBackground(
worktreePath: options.worktreePath, worktreePath: options.worktreePath,
setupTerminalId, setupTerminalId,
}, },
"Background worktree creation failed", "Background worktree setup failed",
); );
} finally { } finally {
await dependencies.emitWorkspaceUpdateForCwd(options.worktreePath); await dependencies.emitWorkspaceUpdateForCwd(options.worktreePath);