From fef479e74909d1b3d5153a25faedf66487cba8e8 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Wed, 29 Jul 2026 10:20:55 +0000 Subject: [PATCH] fix(projects): scope legacy paths to hosts --- packages/app/src/projects/project-key.test.ts | 18 ++++++++++++++++ packages/app/src/projects/project-key.ts | 6 +++--- packages/app/src/utils/projects.test.ts | 21 +++++++++++++------ 3 files changed, 36 insertions(+), 9 deletions(-) create mode 100644 packages/app/src/projects/project-key.test.ts diff --git a/packages/app/src/projects/project-key.test.ts b/packages/app/src/projects/project-key.test.ts new file mode 100644 index 000000000..a04dd9cad --- /dev/null +++ b/packages/app/src/projects/project-key.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, test } from "vitest"; +import { resolveProjectKey } from "./project-key"; + +describe("resolveProjectKey", () => { + test("keeps legacy path-shaped project IDs local to their host", () => { + const resolve = (serverId: string) => + resolveProjectKey({ serverId, projectId: "/workspace/app" }); + + expect(resolve("host-a")).not.toBe(resolve("host-b")); + }); + + test("keeps recognized legacy remote project IDs shared across hosts", () => { + const resolve = (serverId: string) => + resolveProjectKey({ serverId, projectId: "remote:github.com/acme/app" }); + + expect(resolve("host-a")).toBe(resolve("host-b")); + }); +}); diff --git a/packages/app/src/projects/project-key.ts b/packages/app/src/projects/project-key.ts index cc8b1e94d..0a63b0d15 100644 --- a/packages/app/src/projects/project-key.ts +++ b/packages/app/src/projects/project-key.ts @@ -5,9 +5,9 @@ export function resolveProjectKey(input: { }): string { if (input.projectKey) return input.projectKey; // COMPAT(projectKey): added in v0.2.4 on 2026-07-28; remove after 2027-01-28. - // Older daemons used remote/path-shaped project IDs as their grouping key. New opaque IDs - // must remain host-local when the new field is absent. - return input.projectId.startsWith("prj_") ? frameHostProjectKey(input) : input.projectId; + // Older daemons used recognized remote-shaped project IDs as their grouping key. Their + // path-shaped and opaque IDs are host-local when the new field is absent. + return input.projectId.startsWith("remote:") ? input.projectId : frameHostProjectKey(input); } export function frameHostProjectKey(input: { serverId: string; projectId: string }): string { diff --git a/packages/app/src/utils/projects.test.ts b/packages/app/src/utils/projects.test.ts index a63a7d9ce..c8d4a571e 100644 --- a/packages/app/src/utils/projects.test.ts +++ b/packages/app/src/utils/projects.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; import type { ProjectPlacementPayload } from "@getpaseo/protocol/messages"; +import { frameHostProjectKey } from "@/projects/project-key"; import type { WorkspaceDescriptor } from "@/stores/session-store"; import { buildProjects } from "./projects"; @@ -451,7 +452,11 @@ describe("buildProjects", () => { const acme = result.projects.find( (project) => project.projectKey === "remote:github.com/acme/app#subdir:packages/client", ); - const legacy = result.projects.find((project) => project.projectKey === "legacy-project"); + const legacy = result.projects.find( + (project) => + project.projectKey === + frameHostProjectKey({ serverId: "legacy", projectId: "legacy-project" }), + ); expect(acme?.hosts[0]?.repoRoot).toBe("/worktrees/app/main/packages/client"); expect(legacy?.hosts[0]?.repoRoot).toBe("/repo/legacy"); @@ -584,8 +589,8 @@ describe("buildProjects", () => { }); expect(result.projects.map((project) => project.projectKey)).toEqual([ - "/repo/one", - "/repo/two", + frameHostProjectKey({ serverId: "local", projectId: "/repo/one" }), + frameHostProjectKey({ serverId: "local", projectId: "/repo/two" }), ]); }); @@ -646,7 +651,7 @@ describe("buildProjects", () => { "remote:gitlab.com/acme/api", "remote:bitbucket.org/acme/cli", "remote:github.com/acme/web", - "/repo/local", + frameHostProjectKey({ serverId: "local", projectId: "/repo/local" }), ]); }); @@ -739,7 +744,9 @@ describe("buildProjects", () => { expect(result.projects).toHaveLength(1); const summary = result.projects[0]; - expect(summary?.projectKey).toBe("legacy-project"); + expect(summary?.projectKey).toBe( + frameHostProjectKey({ serverId: "old-daemon", projectId: "legacy-project" }), + ); expect(summary?.projectName).toBe("Legacy"); expect(summary?.githubUrl).toBeUndefined(); expect(summary?.hosts).toHaveLength(1); @@ -770,7 +777,9 @@ describe("buildProjects", () => { expect(result.projects).toHaveLength(1); const summary = result.projects[0]; - expect(summary?.projectKey).toBe("/repo/fresh"); + expect(summary?.projectKey).toBe( + frameHostProjectKey({ serverId: "local", projectId: "/repo/fresh" }), + ); expect(summary?.totalWorkspaceCount).toBe(0); expect(summary?.hosts).toHaveLength(1); // repoRoot must be non-empty or the project settings screen treats the host