perf: overhaul session loading, caching, and runtime isolation (#2360)

Improve OpenChamber responsiveness under large session workloads while fixing
cache, synchronization, and persistence correctness across runtimes, projects,
directories, and worktrees.

- prioritize selected and visible sessions during bootstrap and defer
  non-critical enrichment work
- reduce redundant message loading, event processing, store publication, and
  hidden sidebar work
- prevent stale session and message requests from overwriting newer
  authoritative state
- preserve existing data when authoritative fetches fail instead of treating
  failures as successful empty responses
- scope session materialization, messages, drafts, queues, todos, pins,
  permissions, folders, tabs, Git state, and pull request data by runtime and
  directory identity
- harden runtime switching, reconnect, cleanup, mutation reconciliation, and
  persisted-state ordering
- preserve live subagent Task linkage when metadata arrives after an older
  message request or while streaming parts are suspended
- coalesce overlapping tail refreshes without losing newer refresh demand
- improve cold-session loading by moving deferrable work out of the critical
  bootstrap path
- isolate URL authentication, mobile credentials, native secrets, and other
  runtime-owned state across endpoint changes
- bound long-lived caches and remove avoidable allocations from event and
  rendering hot paths
- limit virtualization to archive collections where it improves rendering
  without disrupting active sidebar layout
- stabilize session folders, pin ordering, expanded state, and persisted
  sidebar behavior
- open skill files through the same secure editor and outside-workspace grant
  flow used by file navigation, including worktree sessions
- expand regression coverage for stale completions, runtime collisions,
  reconnect behavior, persistence races, authoritative empty results, and
  subagent refresh ordering
- document the updated synchronization, cache ownership, performance, and
  runtime-isolation invariants
This commit is contained in:
Bohdan Triapitsyn
2026-07-21 20:52:20 +03:00
committed by GitHub
parent 485efc7117
commit 85400459e9
197 changed files with 10835 additions and 3400 deletions
@@ -12,7 +12,10 @@ let questionRejectError: unknown | null = null
let sessionShareResult: { data?: unknown; error?: unknown; response?: { status?: number } } = {}
let sessionUpdateResult: { data?: unknown; error?: unknown; response?: { status?: number } } = {}
let sessionMessagesResult: { data?: unknown; error?: unknown; response?: { status?: number } } = { data: [] }
let sessionDeleteError: unknown | null = null
const globalUpsertedSessions: unknown[] = []
const globalRemovedSessionIds: string[] = []
const deletedCleanupIdentities: Array<{ runtimeKey: string; directory: string; sessionId: string }> = []
const movedSessionDirectories: Array<{ sessionID: string; directory: string }> = []
const mockScopedClient = {
@@ -135,6 +138,11 @@ mock.module("@/lib/opencode/client", () => ({
replyCalls.push({ method: "session.update", params: { sessionID: sessionId, ...changes, directory } })
return Promise.resolve(sessionUpdateResult.data)
}),
deleteSession: mock((sessionId: string, directory?: string | null) => {
replyCalls.push({ method: "session.delete", params: { sessionID: sessionId, directory } })
if (sessionDeleteError) throw sessionDeleteError
return Promise.resolve(true)
}),
},
}))
@@ -157,6 +165,9 @@ mock.module("./session-ui-store", () => ({
if (sessionId === "session-b") return "/other/project"
return null
},
currentSessionId: null,
setCurrentSession: () => {},
setWorktreeMetadata: () => {},
setSessionDirectory: (sessionID: string, directory: string) => {
movedSessionDirectories.push({ sessionID, directory })
},
@@ -185,6 +196,7 @@ mock.module("./input-store", () => ({
}))
mock.module("@/stores/useGlobalSessionsStore", () => ({
resolveGlobalSessionDirectory: (session: SessionWithDirectory) => session.directory ?? session.project?.worktree ?? null,
mergeSessionDirectoryMetadata: (incoming: Session, existing?: SessionWithDirectory | null): SessionWithDirectory => {
if (!existing) return incoming as SessionWithDirectory
const next = { ...(incoming as SessionWithDirectory) }
@@ -197,13 +209,24 @@ mock.module("@/stores/useGlobalSessionsStore", () => ({
},
useGlobalSessionsStore: {
getState: () => ({
activeSessions: [],
archivedSessions: [],
upsertSession: (session: unknown) => {
globalUpsertedSessions.push(session)
},
removeSessions: (ids: Iterable<string>) => {
globalRemovedSessionIds.push(...ids)
},
}),
},
}))
mock.module("./session-deletion-cleanup", () => ({
cleanupPersistedSessionState: (identity: { runtimeKey: string; directory: string; sessionId: string }) => {
deletedCleanupIdentities.push(identity)
},
}))
mock.module("./sync-refs", () => ({
registerSessionDirectory: (sessionID: string, directory: string) => {
registeredSessionDirectories.push({ sessionID, directory })
@@ -328,6 +351,75 @@ describe("moveSessionToDirectory", () => {
})
})
describe("confirmed session removal", () => {
beforeEach(() => {
replyCalls.length = 0
globalUpsertedSessions.length = 0
globalRemovedSessionIds.length = 0
deletedCleanupIdentities.length = 0
sessionDeleteError = null
sessionUpdateResult = {}
})
test("does not remove live or persisted state when delete fails", async () => {
sessionDeleteError = new Error("delete failed")
const source = createStore({}, {
session: [{ id: "session-a", directory: "/test/project", time: { created: 1 } } as Session],
})
const { deleteSession, setActionRefs } = await import("./session-actions")
setActionRefs(mockSdk as unknown as OpencodeClient, createChildStores([["/test/project", source]]), () => "/test/project")
expect(await deleteSession("session-a")).toBe(false)
expect(source.getState().session.map((item) => item.id)).toEqual(["session-a"])
expect(globalRemovedSessionIds).toEqual([])
expect(deletedCleanupIdentities).toEqual([])
})
test("cleans persisted state after the server confirms deletion", async () => {
const source = createStore({}, {
session: [{ id: "session-a", directory: "/test/project", time: { created: 1 } } as Session],
})
const { deleteSession, setActionRefs } = await import("./session-actions")
setActionRefs(mockSdk as unknown as OpencodeClient, createChildStores([["/test/project", source]]), () => "/test/project")
expect(await deleteSession("session-a")).toBe(true)
expect(source.getState().session).toEqual([])
expect(globalRemovedSessionIds).toEqual(["session-a"])
expect(deletedCleanupIdentities).toHaveLength(1)
expect({
directory: deletedCleanupIdentities[0]?.directory,
sessionId: deletedCleanupIdentities[0]?.sessionId,
}).toEqual({ directory: "/test/project", sessionId: "session-a" })
})
test("does not archive locally until the server returns the archived session", async () => {
const source = createStore({}, {
session: [{ id: "session-a", directory: "/test/project", time: { created: 1 } } as Session],
})
const { archiveSession, setActionRefs } = await import("./session-actions")
setActionRefs(mockSdk as unknown as OpencodeClient, createChildStores([["/test/project", source]]), () => "/test/project")
expect(await archiveSession("session-a")).toBe(false)
expect(source.getState().session.map((item) => item.id)).toEqual(["session-a"])
expect(globalUpsertedSessions).toEqual([])
})
test("moves the session to archived state after server confirmation", async () => {
sessionUpdateResult = {
data: { id: "session-a", directory: "/test/project", time: { created: 1, archived: 2 } } as Session,
}
const source = createStore({}, {
session: [{ id: "session-a", directory: "/test/project", time: { created: 1 } } as Session],
})
const { archiveSession, setActionRefs } = await import("./session-actions")
setActionRefs(mockSdk as unknown as OpencodeClient, createChildStores([["/test/project", source]]), () => "/test/project")
expect(await archiveSession("session-a")).toBe(true)
expect(source.getState().session).toEqual([])
expect((globalUpsertedSessions[0] as Session)?.time?.archived).toBe(2)
})
})
describe("fetchMessagesForSession startup race", () => {
test("does not reject before sync action refs are initialized", async () => {
const { fetchMessagesForSession } = await import("./session-actions")