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
+112 -20
View File
@@ -3,6 +3,7 @@ import { persist } from 'zustand/middleware';
import type { GitHubPullRequestStatus, RuntimeAPIs } from '@/lib/api/types';
import { mapWithConcurrency } from '@/lib/concurrency';
import { createDeferredSafeJSONStorage } from './utils/safeStorage';
import { getRuntimeKey } from '@/lib/runtime-switch';
const PR_REVALIDATE_TTL_MS = 90_000;
const PR_REVALIDATE_INTERVAL_MS = 15_000;
@@ -14,6 +15,7 @@ const PR_OPEN_STABLE_INTERVAL_MS = 5 * 60_000;
const PR_STATUS_REFRESH_CONCURRENCY = 4;
const PR_PERSIST_TTL_MS = 12 * 60 * 60_000;
const PR_STATUS_STORAGE_KEY = 'openchamber.github-pr-status';
const PR_MAX_ENTRIES = 200;
const isTerminalPrState = (state: string | null | undefined): boolean => state === 'closed' || state === 'merged';
const isPendingChecks = (status: GitHubPullRequestStatus | null): boolean => {
@@ -24,9 +26,14 @@ const isPendingChecks = (status: GitHubPullRequestStatus | null): boolean => {
return checks.state === 'pending' || checks.pending > 0;
};
const getOpenPrRefreshInterval = (status: GitHubPullRequestStatus | null): number => {
if (isPendingChecks(status)) return PR_OPEN_BUSY_INTERVAL_MS;
if (status?.checks && status.checks.state !== 'pending') return PR_OPEN_STABLE_INTERVAL_MS;
return PR_OPEN_DEFAULT_INTERVAL_MS;
};
export const getGitHubPrStatusKey = (directory: string, branch: string, remoteName?: string | null): string => {
void remoteName;
return `${directory}::${branch}`;
return JSON.stringify([getRuntimeKey(), directory, branch, remoteName ?? 'auto']);
};
type RefreshOptions = {
@@ -43,6 +50,7 @@ type PrTrackingTarget = {
};
type PrRuntimeParams = {
runtimeKey?: string;
directory: string;
branch: string;
remoteName: string | null;
@@ -53,6 +61,7 @@ type PrRuntimeParams = {
};
type PrEntryIdentity = {
runtimeKey: string;
directory: string;
branch: string;
remoteName: string | null;
@@ -69,6 +78,7 @@ type PrStatusEntry = {
params: PrRuntimeParams | null;
identity: PrEntryIdentity | null;
resolvedRemoteName: string | null;
paramsRevision: number;
};
type PersistedPrStatusEntry = Pick<
@@ -87,12 +97,14 @@ type GitHubPrStatusStore = {
refresh: (key: string, options?: RefreshOptions) => Promise<void>;
refreshTargets: (targets: PrTrackingTarget[], options?: RefreshOptions) => Promise<void>;
updateStatus: (key: string, updater: (prev: GitHubPullRequestStatus | null) => GitHubPullRequestStatus | null) => void;
resetForRuntimeSwitch: () => void;
};
const timers = new Map<string, number>();
const bootstrapTimers = new Map<string, number[]>();
const inFlightBySignature = new Set<string>();
const inFlightBySignature = new Map<string, symbol>();
const lastRefreshBySignature = new Map<string, number>();
let prRuntimeGeneration = 0;
// Global concurrency gate for PR-status network requests.
//
@@ -141,6 +153,7 @@ const createEntry = (): PrStatusEntry => ({
params: null,
identity: null,
resolvedRemoteName: null,
paramsRevision: 0,
});
const getIdentityFromEntry = (entry: PrStatusEntry | null | undefined): PrEntryIdentity | null => {
@@ -148,6 +161,7 @@ const getIdentityFromEntry = (entry: PrStatusEntry | null | undefined): PrEntryI
return {
directory: entry.params.directory,
branch: entry.params.branch,
runtimeKey: entry.params.runtimeKey ?? entry.identity?.runtimeKey ?? getRuntimeKey(),
remoteName: entry.params.remoteName ?? entry.resolvedRemoteName ?? entry.identity?.remoteName ?? null,
};
}
@@ -162,7 +176,7 @@ const getSignatureFromEntry = (entry: PrStatusEntry | null | undefined): string
if (!identity?.directory || !identity.branch) {
return null;
}
return `${identity.directory}::${identity.branch}`;
return JSON.stringify([identity.runtimeKey, identity.directory, identity.branch, identity.remoteName ?? 'auto']);
};
const getKeysBySignature = (entries: Record<string, PrStatusEntry>, signature: string): string[] => {
@@ -172,20 +186,35 @@ const getKeysBySignature = (entries: Record<string, PrStatusEntry>, signature: s
};
const mergeParams = (entry: PrStatusEntry, next: PrRuntimeParams): PrStatusEntry => {
const runtimeKey = next.runtimeKey ?? getRuntimeKey();
const remoteName = next.remoteName ?? entry.params?.remoteName ?? entry.resolvedRemoteName ?? entry.identity?.remoteName ?? null;
const paramsChanged = !entry.params
|| entry.params.runtimeKey !== runtimeKey
|| entry.params.directory !== next.directory
|| entry.params.branch !== next.branch
|| entry.params.remoteName !== remoteName
|| entry.params.canShow !== next.canShow
|| entry.params.github !== next.github
|| entry.params.githubAuthChecked !== next.githubAuthChecked
|| entry.params.githubConnected !== next.githubConnected;
return {
...entry,
paramsRevision: paramsChanged ? entry.paramsRevision + 1 : entry.paramsRevision,
...(paramsChanged ? { isLoading: false, error: null } : {}),
params: entry.params
? {
...entry.params,
...next,
runtimeKey,
remoteName,
}
: {
...next,
runtimeKey,
remoteName,
},
identity: {
runtimeKey,
directory: next.directory,
branch: next.branch,
remoteName,
@@ -256,6 +285,20 @@ const hydrateEntry = (entry: PersistedPrStatusEntry | undefined): PrStatusEntry
resolvedRemoteName: entry?.resolvedRemoteName ?? entry?.status?.resolvedRemoteName ?? null,
});
const boundEntries = (entries: Record<string, PrStatusEntry>): Record<string, PrStatusEntry> => {
const all = Object.entries(entries);
if (all.length <= PR_MAX_ENTRIES) return entries;
return Object.fromEntries(all
.sort(([, left], [, right]) => {
const leftProtected = left.watchers > 0 || left.isLoading;
const rightProtected = right.watchers > 0 || right.isLoading;
if (leftProtected !== rightProtected) return leftProtected ? -1 : 1;
return Math.max(right.lastRefreshAt, right.lastDiscoveryPollAt)
- Math.max(left.lastRefreshAt, left.lastDiscoveryPollAt);
})
.slice(0, PR_MAX_ENTRIES));
};
export const useGitHubPrStatusStore = create<GitHubPrStatusStore>()(
persist(
(set, get) => ({
@@ -263,16 +306,36 @@ export const useGitHubPrStatusStore = create<GitHubPrStatusStore>()(
activeRequestCount: 0,
totalRequestCount: 0,
resetForRuntimeSwitch: () => {
prRuntimeGeneration += 1;
for (const timerId of timers.values()) window.clearInterval(timerId);
for (const timerIds of bootstrapTimers.values()) timerIds.forEach((timerId) => window.clearTimeout(timerId));
timers.clear();
bootstrapTimers.clear();
inFlightBySignature.clear();
lastRefreshBySignature.clear();
set((state) => ({
activeRequestCount: 0,
entries: Object.fromEntries(Object.entries(state.entries).map(([key, entry]) => [key, {
...entry,
watchers: 0,
isLoading: false,
params: null,
paramsRevision: entry.paramsRevision + 1,
}])),
}));
},
ensureEntry: (key) => {
set((state) => {
if (state.entries[key]) {
return state;
}
return {
entries: {
entries: boundEntries({
...state.entries,
[key]: createEntry(),
},
}),
};
});
},
@@ -369,11 +432,7 @@ export const useGitHubPrStatusStore = create<GitHubPrStatusStore>()(
}
const elapsed = Date.now() - entry.lastRefreshAt;
const nextInterval = isPendingChecks(entry.status)
? PR_OPEN_BUSY_INTERVAL_MS
: (entry.status?.checks && entry.status.checks.state !== 'pending'
? PR_OPEN_STABLE_INTERVAL_MS
: PR_OPEN_DEFAULT_INTERVAL_MS);
const nextInterval = getOpenPrRefreshInterval(entry.status);
if (elapsed < nextInterval) {
return;
}
@@ -449,8 +508,17 @@ export const useGitHubPrStatusStore = create<GitHubPrStatusStore>()(
return;
}
inFlightBySignature.add(signature);
lastRefreshBySignature.set(signature, Date.now());
const requestToken = Symbol(signature);
const runtimeGeneration = prRuntimeGeneration;
const paramsRevision = entry.paramsRevision;
const runtimeKey = entry.params?.runtimeKey ?? entry.identity?.runtimeKey ?? getRuntimeKey();
const isCurrent = () => (
runtimeGeneration === prRuntimeGeneration
&& runtimeKey === getRuntimeKey()
&& inFlightBySignature.get(signature) === requestToken
&& get().entries[key]?.paramsRevision === paramsRevision
);
inFlightBySignature.set(signature, requestToken);
set((prev) => {
const nextEntries = { ...prev.entries };
@@ -461,7 +529,6 @@ export const useGitHubPrStatusStore = create<GitHubPrStatusStore>()(
}
nextEntries[signatureKey] = {
...current,
lastRefreshAt: Date.now(),
isLoading: options?.silent ? current.isLoading : true,
error: null,
};
@@ -472,6 +539,7 @@ export const useGitHubPrStatusStore = create<GitHubPrStatusStore>()(
});
if (params.githubAuthChecked && params.githubConnected === false) {
if (!isCurrent()) return;
set((prev) => {
const nextEntries = { ...prev.entries };
signatureKeys.forEach((signatureKey) => {
@@ -491,11 +559,12 @@ export const useGitHubPrStatusStore = create<GitHubPrStatusStore>()(
entries: nextEntries,
};
});
inFlightBySignature.delete(signature);
if (inFlightBySignature.get(signature) === requestToken) inFlightBySignature.delete(signature);
return;
}
if (!params.github?.prStatus) {
if (!isCurrent()) return;
set((prev) => {
const nextEntries = { ...prev.entries };
signatureKeys.forEach((signatureKey) => {
@@ -515,7 +584,7 @@ export const useGitHubPrStatusStore = create<GitHubPrStatusStore>()(
entries: nextEntries,
};
});
inFlightBySignature.delete(signature);
if (inFlightBySignature.get(signature) === requestToken) inFlightBySignature.delete(signature);
return;
}
@@ -528,10 +597,16 @@ export const useGitHubPrStatusStore = create<GitHubPrStatusStore>()(
await acquirePrStatusNetworkSlot();
let next: GitHubPullRequestStatus;
try {
if (!isCurrent()) return;
// Failed requests need the same non-forced cooldown as successful
// refreshes. Record only work that reaches the network slot so a
// stale queued request cannot suppress its replacement.
lastRefreshBySignature.set(signature, Date.now());
next = await params.github.prStatus(params.directory, params.branch, params.remoteName ?? undefined, { force: options?.force });
} finally {
releasePrStatusNetworkSlot();
}
if (!isCurrent()) return;
set((prev) => {
const nextEntries = { ...prev.entries };
signatureKeys.forEach((signatureKey) => {
@@ -563,6 +638,7 @@ export const useGitHubPrStatusStore = create<GitHubPrStatusStore>()(
const resolvedRemoteName = status.resolvedRemoteName ?? current.resolvedRemoteName ?? params.remoteName ?? null;
const identity = getIdentityFromEntry(current) ?? {
runtimeKey,
directory: params.directory,
branch: params.branch,
remoteName: params.remoteName ?? null,
@@ -574,6 +650,7 @@ export const useGitHubPrStatusStore = create<GitHubPrStatusStore>()(
error: null,
isLoading: options?.silent ? current.isLoading : false,
isInitialStatusResolved: options?.markInitialResolved === false ? current.isInitialStatusResolved : true,
lastRefreshAt: Date.now(),
resolvedRemoteName,
identity: {
...identity,
@@ -587,6 +664,7 @@ export const useGitHubPrStatusStore = create<GitHubPrStatusStore>()(
};
});
} catch (error) {
if (!isCurrent()) return;
const message = error instanceof Error ? error.message : String(error);
set((prev) => {
const nextEntries = { ...prev.entries };
@@ -607,8 +685,10 @@ export const useGitHubPrStatusStore = create<GitHubPrStatusStore>()(
};
});
} finally {
inFlightBySignature.delete(signature);
set((prev) => ({ ...prev, activeRequestCount: Math.max(0, prev.activeRequestCount - 1) }));
if (inFlightBySignature.get(signature) === requestToken) inFlightBySignature.delete(signature);
if (runtimeGeneration === prRuntimeGeneration) {
set((prev) => ({ ...prev, activeRequestCount: Math.max(0, prev.activeRequestCount - 1) }));
}
}
},
@@ -648,6 +728,8 @@ export const useGitHubPrStatusStore = create<GitHubPrStatusStore>()(
{
name: PR_STATUS_STORAGE_KEY,
storage: createDeferredSafeJSONStorage(),
version: 2,
migrate: (persistedState, version) => version < 2 ? { entries: {} } : persistedState,
partialize: (state) => ({
entries: Object.fromEntries(
Object.entries(state.entries)
@@ -657,8 +739,11 @@ export const useGitHubPrStatusStore = create<GitHubPrStatusStore>()(
return false;
}
const freshness = Math.max(entry.lastRefreshAt, entry.lastDiscoveryPollAt);
return freshness === 0 || Date.now() - freshness < PR_PERSIST_TTL_MS;
return freshness > 0 && Date.now() - freshness < PR_PERSIST_TTL_MS;
})
.sort(([, left], [, right]) => Math.max(right.lastRefreshAt, right.lastDiscoveryPollAt)
- Math.max(left.lastRefreshAt, left.lastDiscoveryPollAt))
.slice(0, PR_MAX_ENTRIES)
.map(([key, entry]) => [key, toPersistedEntry(entry)]),
),
}),
@@ -668,7 +753,14 @@ export const useGitHubPrStatusStore = create<GitHubPrStatusStore>()(
return {
...current,
entries: Object.fromEntries(
Object.entries(persistedEntries).map(([key, entry]) => [key, hydrateEntry(entry)]),
Object.entries(persistedEntries)
.filter(([, entry]) => Boolean(
entry.identity?.runtimeKey
&& Math.max(entry.lastRefreshAt, entry.lastDiscoveryPollAt) > 0
&& Date.now() - Math.max(entry.lastRefreshAt, entry.lastDiscoveryPollAt) < PR_PERSIST_TTL_MS,
))
.slice(0, PR_MAX_ENTRIES)
.map(([key, entry]) => [key, hydrateEntry(entry)]),
),
};
},