fix(projects): preserve conservative identity boundaries

This commit is contained in:
Mohamed Boudra
2026-07-29 11:13:07 +00:00
parent 7b9787a75d
commit ad0939dfbb
4 changed files with 86 additions and 12 deletions

View File

@@ -263,6 +263,41 @@ describe("normalizeWorkspaceDescriptor", () => {
mainRepoRoot: null,
},
});
expect(workspace.projectKey).toBe("remote:github.com/acme/app");
});
it("leaves legacy path-shaped placement keys host-local", () => {
const workspace = normalizeWorkspaceDescriptor({
id: "1",
projectId: "/repo/app",
projectDisplayName: "app",
projectRootPath: "/repo/app",
workspaceDirectory: "/repo/app",
projectKind: "git",
workspaceKind: "local_checkout",
name: "main",
archivingAt: null,
status: "done",
statusEnteredAt: null,
activityAt: null,
diffStat: null,
scripts: [],
project: {
projectKey: "/repo/app",
projectName: "app",
checkout: {
cwd: "/repo/app",
isGit: false,
currentBranch: null,
remoteUrl: null,
worktreeRoot: null,
isPaseoOwnedWorktree: false,
mainRepoRoot: null,
},
},
});
expect(workspace.projectKey).toBeNull();
});
});

View File

@@ -170,8 +170,7 @@ export function normalizeWorkspaceDescriptor(
return {
id: normalizeWorkspaceOpaqueId(payload.id) ?? payload.id,
projectId: payload.projectId,
// COMPAT(projectKey): added in v0.2.4 on 2026-07-29; remove after 2027-01-29.
projectKey: payload.projectKey ?? payload.project?.projectKey ?? null,
projectKey: normalizeWorkspaceProjectKey(payload),
projectDisplayName: payload.projectDisplayName,
projectCustomName: payload.projectCustomName ?? null,
projectRootPath: payload.projectRootPath,
@@ -196,6 +195,15 @@ export function normalizeWorkspaceDescriptor(
};
}
function normalizeWorkspaceProjectKey(payload: WorkspaceDescriptorPayload): string | null {
if (payload.projectKey !== undefined) return payload.projectKey;
// COMPAT(projectKey): added in v0.2.4 on 2026-07-29; remove after 2027-01-29.
// Older daemons used remote-shaped placement keys for cross-host identity. Their path-shaped
// placement keys remain absent here so resolveProjectKey can scope them to the host.
const legacyProjectKey = payload.project?.projectKey;
return legacyProjectKey?.startsWith("remote:") ? legacyProjectKey : null;
}
export interface EmptyProjectDescriptor {
projectId: string;
projectKey?: string | null;