From df494b7e914954b538901aa75b1ad620148ca184 Mon Sep 17 00:00:00 2001 From: Michael Hart Date: Mon, 1 Jun 2026 13:23:50 +1000 Subject: [PATCH 1/2] fix(app): show project sessions before path sync resolves Fixes #30166 Session lists in the web UI filter root sessions against `store.path.directory`. After the sync loading refactor, that path could still be empty on initial page load even when `loadSessions()` had already populated the child store. That caused the current project to appear empty until a later client navigation warmed the directory state. Fall back to the requested directory/worktree until the path query resolves, and add a regression test for the child store path fallback. --- .../context/global-sync/child-store.test.ts | 29 +++++++++++++++++++ .../src/context/global-sync/child-store.ts | 5 ++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/packages/app/src/context/global-sync/child-store.test.ts b/packages/app/src/context/global-sync/child-store.test.ts index 7c4adb5216d9..d627f2c13c20 100644 --- a/packages/app/src/context/global-sync/child-store.test.ts +++ b/packages/app/src/context/global-sync/child-store.test.ts @@ -128,6 +128,35 @@ describe("createChildStoreManager", () => { } }) + test("falls back to the requested directory before the path query resolves", () => { + let manager: ReturnType | undefined + + const dispose = createOwner((owner) => { + manager = createChildStoreManager({ + owner, + isBooting: () => false, + isLoadingSessions: () => false, + onBootstrap() {}, + onMcp() {}, + onDispose() {}, + translate: (key) => key, + queryOptions: queryOptionsApi, + global: { provider }, + }) + }) + + try { + if (!manager) throw new Error("manager required") + + const [store] = manager.child("/project", { bootstrap: false }) + + expect(store.path.directory).toBe("/project") + expect(store.path.worktree).toBe("/project") + } finally { + dispose() + } + }) + test("enables MCP only when requested for the directory", () => { let manager: ReturnType | undefined const offset = queryGroups.length diff --git a/packages/app/src/context/global-sync/child-store.ts b/packages/app/src/context/global-sync/child-store.ts index 99da39ebb0e8..531650e0bd77 100644 --- a/packages/app/src/context/global-sync/child-store.ts +++ b/packages/app/src/context/global-sync/child-store.ts @@ -204,9 +204,8 @@ export function createChildStoreManager(input: { }, config: {}, get path() { - if (pathQuery.isLoading || !pathQuery.data) - return { state: "", config: "", worktree: "", directory: "", home: "" } - return pathQuery.data + if (pathQuery.data?.directory) return pathQuery.data + return { state: "", config: "", worktree: directory, directory, home: "" } }, status: "loading" as const, agent: [], From 8aaa67c14143baed2b95661129b7c0cff27ae1e4 Mon Sep 17 00:00:00 2001 From: LukeParkerDev <10430890+Hona@users.noreply.github.com> Date: Mon, 1 Jun 2026 14:22:17 +1000 Subject: [PATCH 2/2] fix(app): keep unresolved child path metadata truthful --- .../session-list-path-loading.spec.ts | 40 +++++++++++++++++++ .../context/global-sync/child-store.test.ts | 6 +-- .../src/context/global-sync/child-store.ts | 4 +- 3 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 packages/app/e2e/regression/session-list-path-loading.spec.ts diff --git a/packages/app/e2e/regression/session-list-path-loading.spec.ts b/packages/app/e2e/regression/session-list-path-loading.spec.ts new file mode 100644 index 000000000000..1dbc0575f15b --- /dev/null +++ b/packages/app/e2e/regression/session-list-path-loading.spec.ts @@ -0,0 +1,40 @@ +import { expect, test } from "@playwright/test" +import { fixture, pageMessages } from "../smoke/session-timeline.fixture" +import { mockOpenCodeServer } from "../utils/mock-server" + +test("shows loaded sessions before the directory path request resolves", async ({ page }) => { + await mockOpenCodeServer(page, { + sessions: fixture.sessions, + provider: fixture.provider, + directory: fixture.directory, + project: fixture.project, + pageMessages, + }) + + let releasePath!: () => void + const pathBlocked = new Promise((resolve) => { + releasePath = resolve + }) + await page.route("**/path?*", async (route) => { + if (!new URL(route.request().url()).searchParams.has("directory")) return route.fallback() + await pathBlocked + return route.fallback() + }) + + await page.addInitScript((directory) => { + localStorage.setItem( + "opencode.global.dat:server", + JSON.stringify({ + projects: { local: [{ worktree: directory, expanded: true }] }, + lastProject: { local: directory }, + }), + ) + }, fixture.directory) + + await page.goto("/") + try { + await expect(page.getByText(fixture.expected.sourceTitle).first()).toBeVisible({ timeout: 5_000 }) + } finally { + releasePath() + } +}) diff --git a/packages/app/src/context/global-sync/child-store.test.ts b/packages/app/src/context/global-sync/child-store.test.ts index d627f2c13c20..19d43746b1b7 100644 --- a/packages/app/src/context/global-sync/child-store.test.ts +++ b/packages/app/src/context/global-sync/child-store.test.ts @@ -52,7 +52,7 @@ beforeAll(async () => { useQueries: (options: () => { queries: Array<{ enabled?: boolean }> }) => { queryGroups.push(options) return [ - { isLoading: false, data: { state: "", config: "", worktree: "", directory: "", home: "" } }, + { isLoading: true, data: undefined }, { isLoading: false, data: {} }, { isLoading: false, data: [] }, { isLoading: false, data: provider }, @@ -128,7 +128,7 @@ describe("createChildStoreManager", () => { } }) - test("falls back to the requested directory before the path query resolves", () => { + test("provides the requested directory while the path query is pending", () => { let manager: ReturnType | undefined const dispose = createOwner((owner) => { @@ -151,7 +151,7 @@ describe("createChildStoreManager", () => { const [store] = manager.child("/project", { bootstrap: false }) expect(store.path.directory).toBe("/project") - expect(store.path.worktree).toBe("/project") + expect(store.path.worktree).toBe("") } finally { dispose() } diff --git a/packages/app/src/context/global-sync/child-store.ts b/packages/app/src/context/global-sync/child-store.ts index 531650e0bd77..b7b1d72ac051 100644 --- a/packages/app/src/context/global-sync/child-store.ts +++ b/packages/app/src/context/global-sync/child-store.ts @@ -204,8 +204,8 @@ export function createChildStoreManager(input: { }, config: {}, get path() { - if (pathQuery.data?.directory) return pathQuery.data - return { state: "", config: "", worktree: directory, directory, home: "" } + if (pathQuery.data) return pathQuery.data + return { state: "", config: "", worktree: "", directory, home: "" } }, status: "loading" as const, agent: [],