Fix optimistic workspace creation on Android
This commit is contained in:
@@ -238,11 +238,8 @@ function createPaseoWorktreeForMcpTest(options: {
|
||||
},
|
||||
},
|
||||
workspaceGitService,
|
||||
primeWorkspaceGitWatchFingerprints: async () => {},
|
||||
broadcastWorkspaceUpdate: async (workspaceId) => {
|
||||
options.broadcasts.push(workspaceId);
|
||||
},
|
||||
});
|
||||
options.broadcasts.push(result.workspace.workspaceId);
|
||||
options.createdWorkspaceIds?.push(result.workspace.workspaceId);
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -505,9 +505,9 @@ export async function createPaseoDaemon(
|
||||
providerRegistry,
|
||||
github,
|
||||
workspaceGitService,
|
||||
createPaseoWorktree: (input, serviceOptions) => {
|
||||
createPaseoWorktree: async (input, serviceOptions) => {
|
||||
const coreDeps = createWorktreeCoreDeps(github);
|
||||
return createPaseoWorktree(input, {
|
||||
const result = await createPaseoWorktree(input, {
|
||||
...coreDeps,
|
||||
...(serviceOptions?.resolveDefaultBranch
|
||||
? {
|
||||
@@ -517,23 +517,13 @@ export async function createPaseoDaemon(
|
||||
projectRegistry,
|
||||
workspaceRegistry,
|
||||
workspaceGitService,
|
||||
primeWorkspaceGitWatchFingerprints: async (workspace) => {
|
||||
await Promise.all(
|
||||
wsServer
|
||||
?.listActiveSessions()
|
||||
.map((session) =>
|
||||
session.primeWorkspaceGitWatchFingerprintForWorkspace(workspace),
|
||||
) ?? [],
|
||||
);
|
||||
},
|
||||
broadcastWorkspaceUpdate: async (workspaceId) => {
|
||||
await Promise.all(
|
||||
wsServer
|
||||
?.listActiveSessions()
|
||||
.map((session) => session.emitWorkspaceUpdateForWorkspaceId(workspaceId)) ?? [],
|
||||
);
|
||||
},
|
||||
});
|
||||
await Promise.all(
|
||||
wsServer
|
||||
?.listActiveSessions()
|
||||
.map((session) => session.warmWorkspaceGitDataForWorkspace(result.workspace)) ?? [],
|
||||
);
|
||||
return result;
|
||||
},
|
||||
paseoHome: config.paseoHome,
|
||||
callerAgentId,
|
||||
|
||||
@@ -20,11 +20,26 @@ describe("createPaseoWorktree", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("creates a worktree, registers the workspace, primes fingerprints, and broadcasts in order", async () => {
|
||||
test("creates a worktree and registers it in the source workspace project without git snapshot lookup", async () => {
|
||||
const { repoDir, tempDir } = createGitRepo();
|
||||
cleanupPaths.push(tempDir);
|
||||
const events: string[] = [];
|
||||
const deps = createDeps({ events });
|
||||
const sourceProject = createPersistedProjectRecordForTest({
|
||||
projectId: "remote:github.com/acme/repo",
|
||||
rootPath: repoDir,
|
||||
displayName: "acme/repo",
|
||||
});
|
||||
const sourceWorkspace = createPersistedWorkspaceRecordForTest({
|
||||
workspaceId: repoDir,
|
||||
projectId: sourceProject.projectId,
|
||||
cwd: repoDir,
|
||||
kind: "local_checkout",
|
||||
displayName: "main",
|
||||
});
|
||||
deps.projects.set(sourceProject.projectId, sourceProject);
|
||||
deps.workspaces.set(sourceWorkspace.workspaceId, sourceWorkspace);
|
||||
deps.workspaceGitService.getSnapshot = vi.fn(deps.workspaceGitService.getSnapshot);
|
||||
|
||||
const result = await createPaseoWorktree(
|
||||
{
|
||||
@@ -39,15 +54,16 @@ describe("createPaseoWorktree", () => {
|
||||
expect(result.created).toBe(true);
|
||||
expect(result.workspace.cwd).toBe(result.worktree.worktreePath);
|
||||
expect(result.workspace.kind).toBe("worktree");
|
||||
expect(result.workspace.projectId).toBe("remote:github.com/acme/repo");
|
||||
expect(result.workspace.displayName).toBe("feature-one");
|
||||
expect(deps.workspaceGitService.getSnapshot).not.toHaveBeenCalled();
|
||||
expect(events).toEqual([
|
||||
`project:${result.workspace.projectId}`,
|
||||
"project:remote:github.com/acme/repo",
|
||||
`workspace:${result.workspace.workspaceId}`,
|
||||
`prime:${result.workspace.workspaceId}`,
|
||||
`broadcast:${result.workspace.workspaceId}`,
|
||||
]);
|
||||
});
|
||||
|
||||
test("reuses an existing worktree and still upserts and broadcasts", async () => {
|
||||
test("reuses an existing worktree and still upserts the workspace", async () => {
|
||||
const { repoDir, tempDir } = createGitRepo();
|
||||
cleanupPaths.push(tempDir);
|
||||
const paseoHome = path.join(tempDir, ".paseo");
|
||||
@@ -81,7 +97,6 @@ describe("createPaseoWorktree", () => {
|
||||
expect(second.created).toBe(false);
|
||||
expect(second.worktree.worktreePath).toBe(first.worktree.worktreePath);
|
||||
expect(events).toContain(`workspace:${second.workspace.workspaceId}`);
|
||||
expect(events).toContain(`broadcast:${second.workspace.workspaceId}`);
|
||||
});
|
||||
|
||||
test("does not mutate registries or broadcast when core worktree creation fails", async () => {
|
||||
@@ -103,8 +118,6 @@ describe("createPaseoWorktree", () => {
|
||||
|
||||
expect(deps.projects.size).toBe(0);
|
||||
expect(deps.workspaces.size).toBe(0);
|
||||
expect(deps.broadcastWorkspaceUpdate).not.toHaveBeenCalled();
|
||||
expect(deps.primeWorkspaceGitWatchFingerprints).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("keeps direct core worktree creation calls behind the service boundary", () => {
|
||||
@@ -157,12 +170,41 @@ function createDeps(options?: {
|
||||
},
|
||||
},
|
||||
workspaceGitService: createWorkspaceGitServiceStub(),
|
||||
primeWorkspaceGitWatchFingerprints: vi.fn(async (workspace: PersistedWorkspaceRecord) => {
|
||||
events.push(`prime:${workspace.workspaceId}`);
|
||||
}),
|
||||
broadcastWorkspaceUpdate: vi.fn(async (workspaceId: string) => {
|
||||
events.push(`broadcast:${workspaceId}`);
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function createPersistedProjectRecordForTest(input: {
|
||||
projectId: string;
|
||||
rootPath: string;
|
||||
displayName: string;
|
||||
}): PersistedProjectRecord {
|
||||
return {
|
||||
projectId: input.projectId,
|
||||
rootPath: input.rootPath,
|
||||
kind: "git",
|
||||
displayName: input.displayName,
|
||||
createdAt: "2026-04-22T00:00:00.000Z",
|
||||
updatedAt: "2026-04-22T00:00:00.000Z",
|
||||
archivedAt: null,
|
||||
};
|
||||
}
|
||||
|
||||
function createPersistedWorkspaceRecordForTest(input: {
|
||||
workspaceId: string;
|
||||
projectId: string;
|
||||
cwd: string;
|
||||
kind: PersistedWorkspaceRecord["kind"];
|
||||
displayName: string;
|
||||
}): PersistedWorkspaceRecord {
|
||||
return {
|
||||
workspaceId: input.workspaceId,
|
||||
projectId: input.projectId,
|
||||
cwd: input.cwd,
|
||||
kind: input.kind,
|
||||
displayName: input.displayName,
|
||||
createdAt: "2026-04-22T00:00:00.000Z",
|
||||
updatedAt: "2026-04-22T00:00:00.000Z",
|
||||
archivedAt: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,15 +6,7 @@ import {
|
||||
createPersistedProjectRecord,
|
||||
createPersistedWorkspaceRecord,
|
||||
} from "./workspace-registry.js";
|
||||
import {
|
||||
buildProjectPlacementForCwd,
|
||||
deriveProjectKind,
|
||||
deriveProjectRootPath,
|
||||
deriveWorkspaceDisplayName,
|
||||
deriveWorkspaceId,
|
||||
deriveWorkspaceKind,
|
||||
normalizeWorkspaceId,
|
||||
} from "./workspace-registry-model.js";
|
||||
import { deriveProjectGroupingName, normalizeWorkspaceId } from "./workspace-registry-model.js";
|
||||
import {
|
||||
createWorktreeCore,
|
||||
type CreateWorktreeCoreDeps,
|
||||
@@ -44,8 +36,6 @@ export interface CreatePaseoWorktreeDeps extends CreateWorktreeCoreDeps {
|
||||
projectRegistry: Pick<ProjectRegistry, "get" | "upsert">;
|
||||
workspaceRegistry: Pick<WorkspaceRegistry, "get" | "list" | "upsert">;
|
||||
workspaceGitService: WorkspaceGitService;
|
||||
broadcastWorkspaceUpdate: (workspaceId: string) => Promise<void> | void;
|
||||
primeWorkspaceGitWatchFingerprints: (workspace: PersistedWorkspaceRecord) => Promise<void>;
|
||||
}
|
||||
|
||||
export async function createPaseoWorktree(
|
||||
@@ -54,13 +44,13 @@ export async function createPaseoWorktree(
|
||||
): Promise<CreatePaseoWorktreeResult> {
|
||||
const createdWorktree = await createWorktreeCore(input, deps);
|
||||
const workspace = await upsertWorkspaceForWorktree({
|
||||
inputCwd: input.cwd,
|
||||
repoRoot: createdWorktree.repoRoot,
|
||||
worktree: createdWorktree.worktree,
|
||||
deps,
|
||||
});
|
||||
|
||||
deps.github.invalidate({ cwd: createdWorktree.worktree.worktreePath });
|
||||
await deps.primeWorkspaceGitWatchFingerprints(workspace);
|
||||
await deps.broadcastWorkspaceUpdate(workspace.workspaceId);
|
||||
|
||||
return {
|
||||
worktree: createdWorktree.worktree,
|
||||
@@ -72,35 +62,34 @@ export async function createPaseoWorktree(
|
||||
}
|
||||
|
||||
async function upsertWorkspaceForWorktree(options: {
|
||||
inputCwd: string;
|
||||
repoRoot: string;
|
||||
worktree: WorktreeConfig;
|
||||
deps: Pick<
|
||||
CreatePaseoWorktreeDeps,
|
||||
"projectRegistry" | "workspaceRegistry" | "workspaceGitService"
|
||||
>;
|
||||
deps: Pick<CreatePaseoWorktreeDeps, "projectRegistry" | "workspaceRegistry">;
|
||||
}): Promise<PersistedWorkspaceRecord> {
|
||||
const normalizedCwd = normalizeWorkspaceId(options.worktree.worktreePath);
|
||||
const placement = await buildProjectPlacementForCwd({
|
||||
cwd: normalizedCwd,
|
||||
workspaceGitService: options.deps.workspaceGitService,
|
||||
});
|
||||
const workspaceId = deriveWorkspaceId(normalizedCwd, placement.checkout);
|
||||
const now = new Date().toISOString();
|
||||
const existingProject = await options.deps.projectRegistry.get(placement.projectKey);
|
||||
const normalizedInputCwd = normalizeWorkspaceId(options.inputCwd);
|
||||
const normalizedRepoRoot = normalizeWorkspaceId(options.repoRoot);
|
||||
const existingWorkspace = await findWorkspaceByDirectory(
|
||||
normalizedCwd,
|
||||
options.deps.workspaceRegistry,
|
||||
);
|
||||
const sourceProject = await resolveSourceProjectForWorktree({
|
||||
inputCwd: normalizedInputCwd,
|
||||
repoRoot: normalizedRepoRoot,
|
||||
existingWorkspace,
|
||||
deps: options.deps,
|
||||
});
|
||||
const workspaceId = normalizedCwd;
|
||||
const now = new Date().toISOString();
|
||||
|
||||
await options.deps.projectRegistry.upsert(
|
||||
createPersistedProjectRecord({
|
||||
projectId: placement.projectKey,
|
||||
rootPath: deriveProjectRootPath({
|
||||
cwd: normalizedCwd,
|
||||
checkout: placement.checkout,
|
||||
}),
|
||||
kind: deriveProjectKind(placement.checkout),
|
||||
displayName: placement.projectName,
|
||||
createdAt: existingProject?.createdAt ?? now,
|
||||
projectId: sourceProject.projectId,
|
||||
rootPath: sourceProject.rootPath,
|
||||
kind: sourceProject.kind,
|
||||
displayName: sourceProject.displayName,
|
||||
createdAt: sourceProject.createdAt ?? now,
|
||||
updatedAt: now,
|
||||
archivedAt: null,
|
||||
}),
|
||||
@@ -108,13 +97,10 @@ async function upsertWorkspaceForWorktree(options: {
|
||||
|
||||
const workspace = createPersistedWorkspaceRecord({
|
||||
workspaceId,
|
||||
projectId: placement.projectKey,
|
||||
projectId: sourceProject.projectId,
|
||||
cwd: normalizedCwd,
|
||||
kind: deriveWorkspaceKind(placement.checkout),
|
||||
displayName: deriveWorkspaceDisplayName({
|
||||
cwd: normalizedCwd,
|
||||
checkout: placement.checkout,
|
||||
}),
|
||||
kind: "worktree",
|
||||
displayName: options.worktree.branchName || normalizedCwd,
|
||||
createdAt: existingWorkspace?.createdAt ?? now,
|
||||
updatedAt: now,
|
||||
archivedAt: null,
|
||||
@@ -124,6 +110,64 @@ async function upsertWorkspaceForWorktree(options: {
|
||||
return (await options.deps.workspaceRegistry.get(workspace.workspaceId)) ?? workspace;
|
||||
}
|
||||
|
||||
async function resolveSourceProjectForWorktree(options: {
|
||||
inputCwd: string;
|
||||
repoRoot: string;
|
||||
existingWorkspace: PersistedWorkspaceRecord | null;
|
||||
deps: Pick<CreatePaseoWorktreeDeps, "projectRegistry" | "workspaceRegistry">;
|
||||
}): Promise<{
|
||||
projectId: string;
|
||||
rootPath: string;
|
||||
kind: "git";
|
||||
displayName: string;
|
||||
createdAt: string | null;
|
||||
}> {
|
||||
const sourceWorkspace =
|
||||
options.existingWorkspace ??
|
||||
(await findWorkspaceForSource({
|
||||
inputCwd: options.inputCwd,
|
||||
repoRoot: options.repoRoot,
|
||||
workspaceRegistry: options.deps.workspaceRegistry,
|
||||
}));
|
||||
const sourceProject = sourceWorkspace
|
||||
? await options.deps.projectRegistry.get(sourceWorkspace.projectId)
|
||||
: null;
|
||||
|
||||
if (sourceWorkspace) {
|
||||
return {
|
||||
projectId: sourceWorkspace.projectId,
|
||||
rootPath: sourceProject?.rootPath ?? options.repoRoot,
|
||||
kind: "git",
|
||||
displayName:
|
||||
sourceProject?.displayName ?? deriveProjectGroupingName(sourceWorkspace.projectId),
|
||||
createdAt: sourceProject?.createdAt ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
const existingFallbackProject = await options.deps.projectRegistry.get(options.repoRoot);
|
||||
return {
|
||||
projectId: options.repoRoot,
|
||||
rootPath: existingFallbackProject?.rootPath ?? options.repoRoot,
|
||||
kind: "git",
|
||||
displayName:
|
||||
existingFallbackProject?.displayName ?? deriveProjectGroupingName(options.repoRoot),
|
||||
createdAt: existingFallbackProject?.createdAt ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
async function findWorkspaceForSource(options: {
|
||||
inputCwd: string;
|
||||
repoRoot: string;
|
||||
workspaceRegistry: Pick<WorkspaceRegistry, "list">;
|
||||
}): Promise<PersistedWorkspaceRecord | null> {
|
||||
const workspaces = await options.workspaceRegistry.list();
|
||||
return (
|
||||
workspaces.find((workspace) => workspace.cwd === options.inputCwd && !workspace.archivedAt) ??
|
||||
workspaces.find((workspace) => workspace.cwd === options.repoRoot && !workspace.archivedAt) ??
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
async function findWorkspaceByDirectory(
|
||||
cwd: string,
|
||||
workspaceRegistry: Pick<WorkspaceRegistry, "list">,
|
||||
|
||||
@@ -900,6 +900,11 @@ export class Session {
|
||||
await this.emitWorkspaceUpdatesForWorkspaceIds([workspaceId], { skipReconcile: true });
|
||||
}
|
||||
|
||||
async warmWorkspaceGitDataForWorkspace(workspace: PersistedWorkspaceRecord): Promise<void> {
|
||||
await this.primeWorkspaceGitWatchFingerprintForWorkspace(workspace);
|
||||
await this.emitWorkspaceUpdateForWorkspaceId(workspace.workspaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client's current activity state
|
||||
*/
|
||||
@@ -5764,6 +5769,36 @@ export class Session {
|
||||
};
|
||||
}
|
||||
|
||||
private async describeCreatedWorktreeWorkspace(
|
||||
result: CreatePaseoWorktreeResult,
|
||||
): Promise<WorkspaceDescriptorPayload> {
|
||||
const projectRecord = await this.projectRegistry.get(result.workspace.projectId);
|
||||
return {
|
||||
id: result.workspace.workspaceId,
|
||||
projectId: result.workspace.projectId,
|
||||
projectDisplayName: projectRecord?.displayName ?? String(result.workspace.projectId),
|
||||
projectRootPath: projectRecord?.rootPath ?? result.repoRoot,
|
||||
workspaceDirectory: result.workspace.cwd,
|
||||
projectKind: "git",
|
||||
workspaceKind: result.workspace.kind,
|
||||
name: result.worktree.branchName || result.workspace.displayName,
|
||||
status: "done",
|
||||
activityAt: null,
|
||||
diffStat: { additions: 0, deletions: 0 },
|
||||
scripts: [],
|
||||
gitRuntime: {
|
||||
currentBranch: result.worktree.branchName || null,
|
||||
remoteUrl: null,
|
||||
isPaseoOwnedWorktree: true,
|
||||
isDirty: false,
|
||||
aheadBehind: null,
|
||||
aheadOfOrigin: null,
|
||||
behindOfOrigin: null,
|
||||
},
|
||||
githubRuntime: null,
|
||||
};
|
||||
}
|
||||
|
||||
private async buildWorkspaceDescriptor(input: {
|
||||
workspace: PersistedWorkspaceRecord;
|
||||
projectRecord?: PersistedProjectRecord | null;
|
||||
@@ -6252,15 +6287,16 @@ export class Session {
|
||||
projectRegistry: this.projectRegistry,
|
||||
workspaceRegistry: this.workspaceRegistry,
|
||||
workspaceGitService: this.workspaceGitService,
|
||||
primeWorkspaceGitWatchFingerprints: (workspace) =>
|
||||
this.primeWorkspaceGitWatchFingerprintForWorkspace(workspace),
|
||||
broadcastWorkspaceUpdate: (workspaceId) =>
|
||||
this.emitWorkspaceUpdateForWorkspaceId(workspaceId),
|
||||
});
|
||||
await Promise.all([
|
||||
void Promise.all([
|
||||
this.notifyGitMutation(input.cwd, "create-worktree"),
|
||||
this.notifyGitMutation(result.worktree.worktreePath, "create-worktree"),
|
||||
]);
|
||||
]).catch((error) => {
|
||||
this.sessionLogger.warn(
|
||||
{ err: error, cwd: input.cwd, worktreePath: result.worktree.worktreePath },
|
||||
"Failed to warm git snapshots after creating worktree",
|
||||
);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -6758,9 +6794,10 @@ export class Session {
|
||||
return handleCreateWorktreeRequest(
|
||||
{
|
||||
paseoHome: this.paseoHome,
|
||||
describeWorkspaceRecord: (workspace) => this.describeWorkspaceRecordWithGitData(workspace),
|
||||
describeWorkspaceRecord: (result) => this.describeCreatedWorktreeWorkspace(result),
|
||||
emit: (message) => this.emit(message),
|
||||
createPaseoWorktree: (input) => this.createPaseoWorktree(input),
|
||||
warmWorkspaceGitData: (workspace) => this.warmWorkspaceGitDataForWorkspace(workspace),
|
||||
sessionLogger: this.sessionLogger,
|
||||
runWorktreeSetupInBackground: (options) => this.runWorktreeSetupInBackground(options),
|
||||
},
|
||||
|
||||
@@ -236,12 +236,6 @@ function createPaseoWorktreeForTest(options: {
|
||||
},
|
||||
},
|
||||
workspaceGitService,
|
||||
primeWorkspaceGitWatchFingerprints: async (workspace) => {
|
||||
options.events?.push(`prime:${workspace.workspaceId}`);
|
||||
},
|
||||
broadcastWorkspaceUpdate: async (workspaceId) => {
|
||||
options.events?.push(`broadcast:${workspaceId}`);
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -1041,10 +1035,11 @@ describe("handleCreatePaseoWorktreeRequest", () => {
|
||||
await handleCreatePaseoWorktreeRequest(
|
||||
{
|
||||
paseoHome,
|
||||
describeWorkspaceRecord: async (workspace) =>
|
||||
createWorkspaceDescriptor({ workspace, repoDir }),
|
||||
describeWorkspaceRecord: async (result) =>
|
||||
createWorkspaceDescriptor({ workspace: result.workspace, repoDir }),
|
||||
emit: (message) => emitted.push(message),
|
||||
createPaseoWorktree: createPaseoWorktreeForTest({ paseoHome }),
|
||||
warmWorkspaceGitData: async () => {},
|
||||
sessionLogger: logger,
|
||||
runWorktreeSetupInBackground: async () => {},
|
||||
},
|
||||
@@ -1137,7 +1132,6 @@ describe("handleCreatePaseoWorktreeRequest", () => {
|
||||
expect(result.worktreeBootstrap?.worktree.branchName).toBe("feature/review-pr");
|
||||
expect(result.worktreeBootstrap?.worktree.worktreePath).toContain("agent-review-pr-123");
|
||||
expect(events.some((event) => event.startsWith("workspace:"))).toBe(true);
|
||||
expect(events.some((event) => event.startsWith("broadcast:"))).toBe(true);
|
||||
|
||||
const branch = execSync("git branch --show-current", {
|
||||
cwd: result.sessionConfig.cwd,
|
||||
@@ -1281,9 +1275,9 @@ describe("handleCreatePaseoWorktreeRequest", () => {
|
||||
sessionLogger: createLogger(),
|
||||
emit: (message) => emitted.push(message),
|
||||
createPaseoWorktree: createPaseoWorktreeForTest({ paseoHome, events }),
|
||||
describeWorkspaceRecord: vi.fn(async (workspace) => ({
|
||||
id: workspace.workspaceId,
|
||||
projectId: workspace.projectId,
|
||||
describeWorkspaceRecord: vi.fn(async (result) => ({
|
||||
id: result.workspace.workspaceId,
|
||||
projectId: result.workspace.projectId,
|
||||
projectDisplayName: path.basename(repoDir),
|
||||
projectRootPath: repoDir,
|
||||
projectKind: "git",
|
||||
@@ -1291,7 +1285,20 @@ describe("handleCreatePaseoWorktreeRequest", () => {
|
||||
name: "single-call",
|
||||
status: "done",
|
||||
activityAt: null,
|
||||
diffStat: { additions: 0, deletions: 0 },
|
||||
scripts: [],
|
||||
gitRuntime: {
|
||||
currentBranch: "single-call",
|
||||
remoteUrl: null,
|
||||
isPaseoOwnedWorktree: true,
|
||||
isDirty: false,
|
||||
aheadBehind: null,
|
||||
aheadOfOrigin: null,
|
||||
behindOfOrigin: null,
|
||||
},
|
||||
githubRuntime: null,
|
||||
})),
|
||||
warmWorkspaceGitData: async () => {},
|
||||
runWorktreeSetupInBackground: vi.fn(async () => {}),
|
||||
},
|
||||
{
|
||||
@@ -1303,7 +1310,6 @@ describe("handleCreatePaseoWorktreeRequest", () => {
|
||||
);
|
||||
|
||||
expect(events.some((event) => event.startsWith("workspace:"))).toBe(true);
|
||||
expect(events.some((event) => event.startsWith("broadcast:"))).toBe(true);
|
||||
const response = emitted.find(
|
||||
(
|
||||
message,
|
||||
@@ -1321,6 +1327,7 @@ describe("handleCreatePaseoWorktreeRequest", () => {
|
||||
const paseoHome = path.join(tempDir, ".paseo");
|
||||
const emitted: SessionOutboundMessage[] = [];
|
||||
const backgroundWork = vi.fn(async () => {});
|
||||
const warmWorkspaceGitData = vi.fn(async () => {});
|
||||
let registeredWorktreePath: string | null = null;
|
||||
|
||||
try {
|
||||
@@ -1335,9 +1342,10 @@ describe("handleCreatePaseoWorktreeRequest", () => {
|
||||
registeredWorktreePath = result.worktree.worktreePath;
|
||||
return result;
|
||||
},
|
||||
describeWorkspaceRecord: vi.fn(async (workspace) =>
|
||||
createWorkspaceDescriptor({ workspace, repoDir }),
|
||||
describeWorkspaceRecord: vi.fn(async (result) =>
|
||||
createWorkspaceDescriptor({ workspace: result.workspace, repoDir }),
|
||||
),
|
||||
warmWorkspaceGitData,
|
||||
runWorktreeSetupInBackground: backgroundWork,
|
||||
},
|
||||
{
|
||||
@@ -1356,8 +1364,26 @@ describe("handleCreatePaseoWorktreeRequest", () => {
|
||||
);
|
||||
expect(response?.payload.error).toBeNull();
|
||||
expect(response?.payload.workspace?.id).toBeTruthy();
|
||||
expect(emitted.map((message) => message.type).slice(0, 2)).toEqual([
|
||||
"create_paseo_worktree_response",
|
||||
"workspace_update",
|
||||
]);
|
||||
const workspaceUpdate = emitted[1];
|
||||
expect(workspaceUpdate).toMatchObject({
|
||||
type: "workspace_update",
|
||||
payload: {
|
||||
kind: "upsert",
|
||||
workspace: response?.payload.workspace,
|
||||
},
|
||||
});
|
||||
expect(registeredWorktreePath).toBeTruthy();
|
||||
expect(existsSync(registeredWorktreePath!)).toBe(true);
|
||||
expect(warmWorkspaceGitData).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
workspaceId: response?.payload.workspace?.id,
|
||||
cwd: registeredWorktreePath,
|
||||
}),
|
||||
);
|
||||
expect(backgroundWork).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
requestCwd: repoDir,
|
||||
@@ -1386,9 +1412,10 @@ describe("handleCreatePaseoWorktreeRequest", () => {
|
||||
sessionLogger: createLogger(),
|
||||
emit: (message) => emitted.push(message),
|
||||
createPaseoWorktree: createPaseoWorktreeForTest({ paseoHome }),
|
||||
describeWorkspaceRecord: vi.fn(async (workspace) =>
|
||||
createWorkspaceDescriptor({ workspace, repoDir }),
|
||||
describeWorkspaceRecord: vi.fn(async (result) =>
|
||||
createWorkspaceDescriptor({ workspace: result.workspace, repoDir }),
|
||||
),
|
||||
warmWorkspaceGitData: async () => {},
|
||||
runWorktreeSetupInBackground: vi.fn(async () => {}),
|
||||
},
|
||||
{
|
||||
@@ -1426,9 +1453,10 @@ describe("handleCreatePaseoWorktreeRequest", () => {
|
||||
sessionLogger: createLogger(),
|
||||
emit: (message) => emitted.push(message),
|
||||
createPaseoWorktree: createPaseoWorktreeForTest({ paseoHome }),
|
||||
describeWorkspaceRecord: vi.fn(async (workspace) =>
|
||||
createWorkspaceDescriptor({ workspace, repoDir }),
|
||||
describeWorkspaceRecord: vi.fn(async (result) =>
|
||||
createWorkspaceDescriptor({ workspace: result.workspace, repoDir }),
|
||||
),
|
||||
warmWorkspaceGitData: async () => {},
|
||||
runWorktreeSetupInBackground: vi.fn(async () => {}),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -119,10 +119,11 @@ type HandleWorkspaceSetupStatusRequestDependencies = {
|
||||
type HandleCreatePaseoWorktreeRequestDependencies = {
|
||||
paseoHome?: string;
|
||||
describeWorkspaceRecord: (
|
||||
workspace: PersistedWorkspaceRecord,
|
||||
result: CreatePaseoWorktreeResult,
|
||||
) => Promise<WorkspaceDescriptorPayload>;
|
||||
emit: EmitSessionMessage;
|
||||
createPaseoWorktree: (input: CreatePaseoWorktreeInput) => Promise<CreatePaseoWorktreeResult>;
|
||||
warmWorkspaceGitData: (workspace: PersistedWorkspaceRecord) => Promise<void>;
|
||||
sessionLogger: Logger;
|
||||
runWorktreeSetupInBackground: (options: {
|
||||
requestCwd: string;
|
||||
@@ -604,7 +605,7 @@ export async function handleCreatePaseoWorktreeRequest(
|
||||
const slug = basename(createdWorktree.worktree.worktreePath);
|
||||
const workspace = createdWorktree.workspace;
|
||||
|
||||
const descriptor = await dependencies.describeWorkspaceRecord(workspace);
|
||||
const descriptor = await dependencies.describeWorkspaceRecord(createdWorktree);
|
||||
dependencies.emit({
|
||||
type: "create_paseo_worktree_response",
|
||||
payload: {
|
||||
@@ -614,7 +615,20 @@ export async function handleCreatePaseoWorktreeRequest(
|
||||
requestId: request.requestId,
|
||||
},
|
||||
});
|
||||
dependencies.emit({
|
||||
type: "workspace_update",
|
||||
payload: {
|
||||
kind: "upsert",
|
||||
workspace: descriptor,
|
||||
},
|
||||
});
|
||||
|
||||
void dependencies.warmWorkspaceGitData(workspace).catch((error) => {
|
||||
dependencies.sessionLogger.warn(
|
||||
{ err: error, workspaceId: workspace.workspaceId },
|
||||
"Failed to warm workspace git data after creating worktree",
|
||||
);
|
||||
});
|
||||
void dependencies.runWorktreeSetupInBackground({
|
||||
requestCwd: request.cwd,
|
||||
repoRoot: createdWorktree.repoRoot,
|
||||
|
||||
Reference in New Issue
Block a user