diff --git a/packages/app/src/utils/host-routes.test.ts b/packages/app/src/utils/host-routes.test.ts index 830192816..cc20ecf3e 100644 --- a/packages/app/src/utils/host-routes.test.ts +++ b/packages/app/src/utils/host-routes.test.ts @@ -216,6 +216,14 @@ describe("projects settings routes", () => { expect(decodeURIComponent(segment)).toBe(projectKey); }); + it("preserves whitespace in opaque project keys", () => { + const projectKey = "host:6:host-a:project:10:/repo/foo "; + const route = buildProjectSettingsRoute(projectKey); + const segment = route.slice("/settings/projects/".length); + + expect(decodeURIComponent(segment)).toBe(projectKey); + }); + it("keeps an already-decoded opaque project key unchanged", () => { const projectKey = "remote:github.com/acme/app#subdir:packages/my%20app"; expect(normalizeProjectSettingsRouteKey(projectKey)).toBe(projectKey); diff --git a/packages/app/src/utils/host-routes.ts b/packages/app/src/utils/host-routes.ts index c05d3c283..39e159f63 100644 --- a/packages/app/src/utils/host-routes.ts +++ b/packages/app/src/utils/host-routes.ts @@ -567,11 +567,10 @@ export function buildProjectsSettingsRoute() { } export function buildProjectSettingsRoute(projectKey: string) { - const normalized = trimNonEmpty(projectKey); - if (!normalized) { + if (!projectKey.trim()) { throw new Error("buildProjectSettingsRoute requires a non-empty projectKey"); } - return `/settings/projects/${encodeSegment(normalized)}` as const; + return `/settings/projects/${encodeSegment(projectKey)}` as const; } export function normalizeProjectSettingsRouteKey(value: string | string[] | undefined): string { diff --git a/packages/server/src/server/session.test.ts b/packages/server/src/server/session.test.ts index 29cb619b0..d86cf4d29 100644 --- a/packages/server/src/server/session.test.ts +++ b/packages/server/src/server/session.test.ts @@ -3417,7 +3417,7 @@ describe("session workspace descriptors", () => { const messages: unknown[] = []; const workspace = { workspaceId: "ws-gh", - projectId: "remote:github.com/acme/app", + projectId: "prj_app", cwd: "/repo/app", kind: "local_checkout" as const, displayName: "app", @@ -3425,7 +3425,8 @@ describe("session workspace descriptors", () => { archivedAt: null, }; const project = { - projectId: "remote:github.com/acme/app", + projectId: "prj_app", + projectKey: "remote:github.com/acme/app", rootPath: "/repo/app", kind: "git" as const, displayName: "acme/app", @@ -3436,7 +3437,10 @@ describe("session workspace descriptors", () => { const session = createSessionForTest({ messages, workspaceRegistry: { get: vi.fn(), list: vi.fn().mockResolvedValue([workspace]) }, - projectRegistry: { list: vi.fn().mockResolvedValue([project]), get: vi.fn() }, + projectRegistry: { + list: vi.fn().mockResolvedValue([project]), + get: vi.fn().mockResolvedValue(project), + }, workspaceGitService: { getSnapshot: vi.fn(), peekSnapshot: vi.fn(() => @@ -3465,8 +3469,10 @@ describe("session workspace descriptors", () => { entries: [ expect.objectContaining({ id: "ws-gh", + projectId: "prj_app", + projectKey: "remote:github.com/acme/app", project: expect.objectContaining({ - projectKey: "remote:github.com/acme/app", + projectKey: "prj_app", projectName: "acme/app", workspaceName: "app", checkout: expect.objectContaining({ diff --git a/packages/server/src/server/session.ts b/packages/server/src/server/session.ts index 8533e7d83..b2dc3612b 100644 --- a/packages/server/src/server/session.ts +++ b/packages/server/src/server/session.ts @@ -1691,7 +1691,7 @@ export class Session { fallbackWorktreeRoot: snapshot?.git.repoRoot, }); return { - projectKey: project.projectKey ?? project.projectId, + projectKey: project.projectId, projectName: resolveProjectDisplayName(project), workspaceName: resolveWorkspaceDisplayName(workspace), checkout, @@ -4206,6 +4206,9 @@ export class Session { return { id: workspace.workspaceId, projectId: workspace.projectId, + ...(resolvedProjectRecord?.projectKey + ? { projectKey: resolvedProjectRecord.projectKey } + : {}), projectDisplayName: resolvedProjectRecord ? resolveProjectDisplayName(resolvedProjectRecord) : workspace.projectId,