Show New Workspace isolation controls immediately (#2702)

* fix(workspaces): show isolation without checkout delay

* fix(workspaces): trust hydrated project capability

* fix(workspaces): resolve branch before creation

* fix(workspaces): honor invalidated checkout status

* fix(workspaces): preserve isolation while projects load

* fix(workspaces): keep provisional project routes usable

Route-derived projects do not know their worktree capability. Represent that uncertainty at the placement boundary so remembered worktree intent survives hydration without disabling the New Workspace screen indefinitely.
This commit is contained in:
Mohamed Boudra
2026-07-31 17:50:35 +02:00
committed by GitHub
parent 35f985baff
commit de44c8cd66
20 changed files with 333 additions and 105 deletions
@@ -44,7 +44,11 @@ function project(overrides: ProjectOverrides = {}): SidebarProjectEntry {
const projectKind = overrides.projectKind ?? "git";
const hosts = Array.from(
overrides.hosts ?? [
{ serverId: "srv", iconWorkingDir: "/repo", canCreateWorktree: projectKind === "git" },
{
serverId: "srv",
iconWorkingDir: "/repo",
worktreeSupport: projectKind === "git" ? "supported" : "unsupported",
},
],
(host) => Object.assign({}, host, { projectId: host.projectId ?? `project-${host.serverId}` }),
);
@@ -135,8 +139,12 @@ describe("buildSidebarProjectRowModel", () => {
const result = buildSidebarProjectRowModel({
project: project({
hosts: [
{ serverId: "host-a", iconWorkingDir: "/repo/a", canCreateWorktree: false },
{ serverId: "host-b", iconWorkingDir: "/repo/b", canCreateWorktree: true },
{
serverId: "host-a",
iconWorkingDir: "/repo/a",
worktreeSupport: "unsupported" as const,
},
{ serverId: "host-b", iconWorkingDir: "/repo/b", worktreeSupport: "supported" as const },
],
}),
collapsed: false,
@@ -155,8 +163,16 @@ describe("buildSidebarProjectRowModel", () => {
project: project({
projectKind: "directory",
hosts: [
{ serverId: "host-a", iconWorkingDir: "/repo/a", canCreateWorktree: false },
{ serverId: "host-b", iconWorkingDir: "/repo/b", canCreateWorktree: false },
{
serverId: "host-a",
iconWorkingDir: "/repo/a",
worktreeSupport: "unsupported" as const,
},
{
serverId: "host-b",
iconWorkingDir: "/repo/b",
worktreeSupport: "unsupported" as const,
},
],
}),
collapsed: false,
@@ -197,8 +213,8 @@ describe("buildSidebarProjectRowModel", () => {
const iconTarget = resolveSidebarProjectIconTarget(
project({
hosts: [
{ serverId: "host-b", iconWorkingDir: "/repo/b", canCreateWorktree: true },
{ serverId: "host-a", iconWorkingDir: "/repo/a", canCreateWorktree: true },
{ serverId: "host-b", iconWorkingDir: "/repo/b", worktreeSupport: "supported" as const },
{ serverId: "host-a", iconWorkingDir: "/repo/a", worktreeSupport: "supported" as const },
],
}),
);
@@ -214,7 +230,9 @@ describe("buildSidebarProjectRowModel", () => {
const [iconTarget] = resolveSidebarProjectIconTargets([
project({
viewKey: '["placement","host-b","project-b"]',
hosts: [{ serverId: "host-b", iconWorkingDir: "/repo/b", canCreateWorktree: true }],
hosts: [
{ serverId: "host-b", iconWorkingDir: "/repo/b", worktreeSupport: "supported" as const },
],
}),
]);
@@ -230,8 +248,12 @@ describe("buildSidebarProjectRowModel", () => {
const groupedProject = project({
iconWorkingDir: "/remote/repo",
hosts: [
{ serverId: "remote", iconWorkingDir: "/remote/repo", canCreateWorktree: true },
{ serverId: "local", iconWorkingDir: "/local/repo", canCreateWorktree: true },
{
serverId: "remote",
iconWorkingDir: "/remote/repo",
worktreeSupport: "supported" as const,
},
{ serverId: "local", iconWorkingDir: "/local/repo", worktreeSupport: "supported" as const },
],
});
@@ -75,7 +75,10 @@ function resolveNewWorkspaceTarget(
supportsMultiplicityByServerId: ReadonlyMap<string, boolean>,
): SidebarProjectHostTarget | null {
for (const host of project.hosts) {
if (!host.canCreateWorktree && !supportsMultiplicityByServerId.get(host.serverId)) {
if (
host.worktreeSupport === "unsupported" &&
!supportsMultiplicityByServerId.get(host.serverId)
) {
continue;
}
const target = hostTarget(host);
@@ -55,7 +55,7 @@ function project(projectKey: string, workspaces: SidebarWorkspaceEntry[]): Sideb
serverId: workspaces[0]?.serverId ?? "s1",
projectId: projectKey,
iconWorkingDir: workspaces[0]?.workspaceDirectory ?? "",
canCreateWorktree: true,
worktreeSupport: "supported" as const,
},
],
workspaces,
@@ -150,7 +150,7 @@ describe("buildSidebarShortcutModel", () => {
directoryProject.projectKind = "directory";
directoryProject.hosts = directoryProject.hosts.map((host) => ({
...host,
canCreateWorktree: false,
worktreeSupport: "unsupported" as const,
}));
const model = buildSidebarShortcutModel({