From 8d1eeac01af705d8152417bb8910237ea8124426 Mon Sep 17 00:00:00 2001 From: Logan Rupe Date: Thu, 3 Sep 2026 16:56:40 +1000 Subject: [PATCH] fix(server): surface workspaceFile in project snapshot queries The projection snapshot queries never selected the workspace_file column and never mapped it, so workspaceFile silently dropped out of every client-facing project shell. Open-in fell back to the folder, the sidebar never showed the workspace icon, and Manage folders never appeared (#185). Select the column in the three project SELECTs and map it in mapProjectShellRow plus the three inline OrchestrationProject literals that build read models from the same rows. --- .../Layers/ProjectionSnapshotQuery.test.ts | 16 ++++++++++++++++ .../Layers/ProjectionSnapshotQuery.ts | 9 +++++++++ 2 files changed, 25 insertions(+) diff --git a/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts b/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts index be874453a0f8..15e4c95f7b98 100644 --- a/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts +++ b/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts @@ -54,6 +54,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => { project_id, title, workspace_root, + workspace_file, default_model_selection_json, scripts_json, created_at, @@ -64,6 +65,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => { 'project-1', 'Project 1', '/tmp/project-1', + '/tmp/project-1/project-1.code-workspace', '{"provider":"codex","model":"gpt-5-codex"}', '[{"id":"script-1","name":"Build","command":"bun run build","icon":"build","runOnWorktreeCreate":false}]', '2026-02-24T00:00:00.000Z', @@ -272,6 +274,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => { id: asProjectId("project-1"), title: "Project 1", workspaceRoot: "/tmp/project-1", + workspaceFile: "/tmp/project-1/project-1.code-workspace", repoRoots: ["/tmp/project-1"], repositoryIdentity: null, repositoryIdentities: [], @@ -402,6 +405,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => { id: asProjectId("project-1"), title: "Project 1", workspaceRoot: "/tmp/project-1", + workspaceFile: "/tmp/project-1/project-1.code-workspace", repoRoots: ["/tmp/project-1"], repositoryIdentity: null, repositoryIdentities: [], @@ -738,6 +742,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => { project_id, title, workspace_root, + workspace_file, default_model_selection_json, scripts_json, created_at, @@ -749,6 +754,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => { 'project-active', 'Active Project', '/tmp/workspace', + '/tmp/workspace/active.code-workspace', '{"provider":"codex","model":"gpt-5-codex"}', '[]', '2026-03-01T00:00:00.000Z', @@ -760,6 +766,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => { 'Deleted Project', '/tmp/deleted', NULL, + NULL, '[]', '2026-03-01T00:00:02.000Z', '2026-03-01T00:00:03.000Z', @@ -841,6 +848,15 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => { assert.equal(project._tag, "Some"); if (project._tag === "Some") { assert.equal(project.value.id, asProjectId("project-active")); + assert.equal(project.value.workspaceFile, "/tmp/workspace/active.code-workspace"); + } + + const projectShell = yield* snapshotQuery.getProjectShellById( + asProjectId("project-active"), + ); + assert.equal(projectShell._tag, "Some"); + if (projectShell._tag === "Some") { + assert.equal(projectShell.value.workspaceFile, "/tmp/workspace/active.code-workspace"); } const missingProject = yield* snapshotQuery.getActiveProjectByWorkspaceRoot("/tmp/missing"); diff --git a/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts b/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts index 4026d976cd84..3199decb40ef 100644 --- a/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts +++ b/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts @@ -334,6 +334,7 @@ function mapProjectShellRow( id: row.projectId, title: row.title, workspaceRoot: row.workspaceRoot, + ...(row.workspaceFile ? { workspaceFile: row.workspaceFile } : {}), repoRoots: normalizeRepoRoots(row), repositoryIdentity: repositoryIdentities[0] ?? null, repositoryIdentities, @@ -422,6 +423,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () { project_id AS "projectId", title, workspace_root AS "workspaceRoot", + workspace_file AS "workspaceFile", repo_roots AS "repoRoots", default_model_selection_json AS "defaultModelSelection", default_thread_env_mode AS "defaultThreadEnvMode", @@ -887,6 +889,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () { project_id AS "projectId", title, workspace_root AS "workspaceRoot", + workspace_file AS "workspaceFile", repo_roots AS "repoRoots", default_model_selection_json AS "defaultModelSelection", default_thread_env_mode AS "defaultThreadEnvMode", @@ -912,6 +915,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () { project_id AS "projectId", title, workspace_root AS "workspaceRoot", + workspace_file AS "workspaceFile", repo_roots AS "repoRoots", default_model_selection_json AS "defaultModelSelection", default_thread_env_mode AS "defaultThreadEnvMode", @@ -1722,6 +1726,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () { id: row.projectId, title: row.title, workspaceRoot: row.workspaceRoot, + ...(row.workspaceFile ? { workspaceFile: row.workspaceFile } : {}), repoRoots: normalizeRepoRoots(row), repositoryIdentity: projectIdentities[0] ?? null, repositoryIdentities: projectIdentities, @@ -1862,6 +1867,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () { id: row.projectId, title: row.title, workspaceRoot: row.workspaceRoot, + ...(row.workspaceFile ? { workspaceFile: row.workspaceFile } : {}), repoRoots: normalizeRepoRoots(row), repositoryIdentities: [], defaultModelSelection: row.defaultModelSelection, @@ -2371,6 +2377,9 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () { id: option.value.projectId, title: option.value.title, workspaceRoot: option.value.workspaceRoot, + ...(option.value.workspaceFile + ? { workspaceFile: option.value.workspaceFile } + : {}), repoRoots: normalizeRepoRoots(option.value), repositoryIdentity: projectIdentities[0] ?? null, repositoryIdentities: projectIdentities,