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:
committed by
GitHub
parent
485efc7117
commit
85400459e9
@@ -1,6 +1,7 @@
|
||||
import { beforeEach, describe, expect, test } from 'bun:test';
|
||||
import type { GitStatus } from '@/lib/api/types';
|
||||
import { useGitStore } from './useGitStore';
|
||||
import { getRuntimeKey } from '@/lib/runtime-switch';
|
||||
|
||||
type Deferred<T> = {
|
||||
promise: Promise<T>;
|
||||
@@ -39,7 +40,7 @@ const createDirectoryState = (status: GitStatus): DirectoryGitState => ({
|
||||
identity: null,
|
||||
diffCache: new Map(),
|
||||
indexRevision: 0,
|
||||
lastRepoCheckAt: 0,
|
||||
lastRepoCheckAt: Date.now(),
|
||||
lastStatusFetch: 0,
|
||||
lastStatusChange: 0,
|
||||
lastLogFetch: 0,
|
||||
@@ -70,13 +71,11 @@ const createGitApi = (getGitStatus: GitAPI['getGitStatus']): GitAPI => ({
|
||||
|
||||
describe('useGitStore', () => {
|
||||
beforeEach(() => {
|
||||
useGitStore.setState({
|
||||
directories: new Map(),
|
||||
activeDirectory: null,
|
||||
});
|
||||
useGitStore.getState().resetForRuntimeSwitch(getRuntimeKey());
|
||||
});
|
||||
|
||||
test('does not reuse an in-flight light status request for full status', async () => {
|
||||
setDirectoryStatus(createStatus());
|
||||
const requests: Deferred<GitStatus>[] = [];
|
||||
const statusCalls: Array<{ directory: string; options?: { mode?: 'light' } }> = [];
|
||||
const git = createGitApi((directory, options) => {
|
||||
@@ -95,12 +94,18 @@ describe('useGitStore', () => {
|
||||
{ directory: '/repo', options: undefined },
|
||||
]);
|
||||
|
||||
requests[0].resolve(createStatus());
|
||||
requests[1].resolve(createStatus({ 'src/index.ts': { insertions: 1, deletions: 0 } }));
|
||||
await Promise.all([lightPromise, fullPromise]);
|
||||
await fullPromise;
|
||||
requests[0].resolve(createStatus());
|
||||
await lightPromise;
|
||||
|
||||
expect(useGitStore.getState().getDirectoryState('/repo')?.status?.diffStats).toEqual({
|
||||
'src/index.ts': { insertions: 1, deletions: 0 },
|
||||
});
|
||||
});
|
||||
|
||||
test('reuses an in-flight full status request for light status', async () => {
|
||||
setDirectoryStatus(createStatus());
|
||||
const requests: Deferred<GitStatus>[] = [];
|
||||
const statusCalls: Array<{ directory: string; options?: { mode?: 'light' } }> = [];
|
||||
const git = createGitApi((directory, options) => {
|
||||
@@ -121,6 +126,59 @@ describe('useGitStore', () => {
|
||||
expect(lightResult).toBe(fullResult);
|
||||
});
|
||||
|
||||
test('does not let an older status fetch undo an optimistic mutation', async () => {
|
||||
const initial = createStatus(undefined, [{ path: 'src/index.ts', index: ' ', working_dir: 'M' }]);
|
||||
setDirectoryStatus(initial);
|
||||
const request = createDeferred<GitStatus>();
|
||||
const git = createGitApi(() => request.promise);
|
||||
|
||||
const loading = useGitStore.getState().fetchStatus('/repo', git, { silent: true });
|
||||
useGitStore.getState().moveStatusPathsOptimistically('/repo', ['src/index.ts'], 'stage');
|
||||
request.resolve(initial);
|
||||
await loading;
|
||||
|
||||
expect(useGitStore.getState().getDirectoryState('/repo')?.status?.files).toEqual([
|
||||
{ path: 'src/index.ts', index: 'M', working_dir: ' ' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('rejects an old runtime completion after reset', async () => {
|
||||
setDirectoryStatus(createStatus());
|
||||
const request = createDeferred<GitStatus>();
|
||||
const git = createGitApi(() => request.promise);
|
||||
const loading = useGitStore.getState().fetchStatus('/repo', git, { silent: true });
|
||||
|
||||
useGitStore.getState().resetForRuntimeSwitch('runtime-b');
|
||||
request.resolve(createStatus(undefined, [{ path: 'stale.ts', index: 'M', working_dir: ' ' }]));
|
||||
await loading;
|
||||
|
||||
expect(useGitStore.getState().runtimeKey).toBe('runtime-b');
|
||||
expect(useGitStore.getState().getDirectoryState('/repo')?.status ?? null).toBe(null);
|
||||
});
|
||||
|
||||
test('rejects direct diff commits captured for another runtime', () => {
|
||||
useGitStore.getState().setDiff('/repo', 'stale.ts', { original: 'a', modified: 'b' }, 'runtime-a');
|
||||
expect(useGitStore.getState().getDiff('/repo', 'stale.ts')).toBe(null);
|
||||
});
|
||||
|
||||
test('keeps the newest branch request when completions are reversed', async () => {
|
||||
const requests = [createDeferred<Awaited<ReturnType<GitAPI['getGitBranches']>>>(), createDeferred<Awaited<ReturnType<GitAPI['getGitBranches']>>>()];
|
||||
let index = 0;
|
||||
const git = {
|
||||
...createGitApi(async () => createStatus()),
|
||||
getGitBranches: () => requests[index++].promise,
|
||||
};
|
||||
const first = useGitStore.getState().fetchBranches('/repo', git);
|
||||
const second = useGitStore.getState().fetchBranches('/repo', git);
|
||||
|
||||
requests[1].resolve({ all: ['new'], current: 'new', branches: {} });
|
||||
await second;
|
||||
requests[0].resolve({ all: ['old'], current: 'old', branches: {} });
|
||||
await first;
|
||||
|
||||
expect(useGitStore.getState().getDirectoryState('/repo')?.branches?.current).toBe('new');
|
||||
});
|
||||
|
||||
test('optimistically stages modified files and preserves untouched file references', () => {
|
||||
const target = { path: 'src/index.ts', index: ' ', working_dir: 'M' };
|
||||
const untouched = { path: 'README.md', index: ' ', working_dir: 'M' };
|
||||
|
||||
Reference in New Issue
Block a user