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
@@ -4,15 +4,16 @@ import { ChatErrorBoundary } from '@/components/chat/ChatErrorBoundary';
import { useSessionUIStore } from '@/sync/session-ui-store';
type ChatViewProps = {
active?: boolean;
readOnly?: boolean;
};
export const ChatView: React.FC<ChatViewProps> = ({ readOnly = false }) => {
export const ChatView: React.FC<ChatViewProps> = ({ active = true, readOnly = false }) => {
const currentSessionId = useSessionUIStore((state) => state.currentSessionId);
return (
<ChatErrorBoundary sessionId={currentSessionId || undefined}>
<ChatContainer readOnly={readOnly} />
<ChatContainer active={active} readOnly={readOnly} />
</ChatErrorBoundary>
);
};
@@ -3,6 +3,7 @@ import React from 'react';
import { useUIStore } from '@/stores/useUIStore';
import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory';
import { useGitStore, useGitStatus, useIsGitRepo, useGitLoadingStatus } from '@/stores/useGitStore';
import { getRuntimeKey } from '@/lib/runtime-switch';
import { cn } from '@/lib/utils';
import type { GitStatus } from '@/lib/api/types';
import {
@@ -667,6 +668,7 @@ const MultiFileDiffEntry = React.memo<MultiFileDiffEntryProps>(({
setIsLoading(true);
let cancelled = false;
const runtimeKey = getRuntimeKey();
const contextLines = loadFullFiles ? FULL_CONTEXT_DIFF_LINES : DEFAULT_CONTEXT_DIFF_LINES;
const fetchPromise = isImageFile(file.path)
? git.getGitFileDiff(directory, { path: file.path, staged })
@@ -697,7 +699,7 @@ const MultiFileDiffEntry = React.memo<MultiFileDiffEntryProps>(({
if (staged) {
setStagedDiffData(nextDiff);
} else {
setDiff(directory, file.path, nextDiff);
setDiff(directory, file.path, nextDiff, runtimeKey);
}
}
setIsLoading(false);
@@ -1494,6 +1496,7 @@ export const DiffView: React.FC<DiffViewProps> = ({
}
setOpeningEditorFilePath(filePath);
const runtimeKey = getRuntimeKey();
try {
let targetLine: number | null = null;
@@ -1525,7 +1528,7 @@ export const DiffView: React.FC<DiffViewProps> = ({
isBinary: response.isBinary,
};
if (!activeDiffStaged) {
setDiff(effectiveDirectory, filePath, diffForNavigation);
setDiff(effectiveDirectory, filePath, diffForNavigation, runtimeKey);
}
}
+16 -10
View File
@@ -567,9 +567,9 @@ const FileRow: React.FC<FileRowProps> = ({
onOpenChange={(open) => setContextMenuPath(open ? node.path : null)}
>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
<Button
variant="ghost"
size="icon"
className="size-6"
onClick={handleMenuButtonClick}
>
@@ -810,7 +810,6 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
const openPaths = useFilesViewTabsStore((state) => (root ? (state.byRoot[root]?.openPaths ?? EMPTY_PATHS) : EMPTY_PATHS));
const selectedPath = useFilesViewTabsStore((state) => (root ? (state.byRoot[root]?.selectedPath ?? null) : null));
const expandedPaths = useFilesViewTabsStore((state) => (root ? (state.byRoot[root]?.expandedPaths ?? EMPTY_PATHS) : EMPTY_PATHS));
const addOpenPath = useFilesViewTabsStore((state) => state.addOpenPath);
const removeOpenPath = useFilesViewTabsStore((state) => state.removeOpenPath);
const removeOpenPathsByPrefix = useFilesViewTabsStore((state) => state.removeOpenPathsByPrefix);
const removeExpandedPathsByPrefix = useFilesViewTabsStore((state) => state.removeExpandedPathsByPrefix);
@@ -922,6 +921,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
const diagramEditorRef = React.useRef<React.ComponentRef<typeof DiagramEditor>>(null);
const lastLoadedFileStatRef = React.useRef<FileStatSnapshot | null>(null);
const activeFileLoadIdRef = React.useRef(0);
const loadingFilePathRef = React.useRef<string | null>(null);
const [autoSaveStatus, setAutoSaveStatus] = React.useState<'idle' | 'saved'>('idle');
const [diagramSaved, setDiagramSaved] = React.useState(false);
const [autoSaveEnabled, setAutoSaveEnabled] = React.useState(getInitialAutoSaveEnabled);
@@ -1952,7 +1952,6 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
if (root) {
setSelectedPath(root, node.path);
addOpenPath(root, node.path);
void ensurePathVisible(node.path, false);
}
@@ -1966,7 +1965,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
if (isMobile) {
setShowMobilePageContent(true);
}
}, [addOpenPath, ensurePathVisible, isDirty, isMobile, root, setSelectedPath]);
}, [ensurePathVisible, isDirty, isMobile, root, setSelectedPath]);
React.useEffect(() => {
if (!selectedFile?.path) {
@@ -1979,16 +1978,23 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
React.useEffect(() => {
if (!selectedFile) {
activeFileLoadIdRef.current += 1;
loadingFilePathRef.current = null;
setFileLoading(false);
return;
}
if (loadedFilePath === selectedFile.path) {
if (loadedFilePath === selectedFile.path || loadingFilePathRef.current === selectedFile.path) {
return;
}
// Selection changes are guarded; this effect is also what restores persisted tabs on mount.
void loadSelectedFile(selectedFile);
const loadingPath = selectedFile.path;
loadingFilePathRef.current = loadingPath;
void loadSelectedFile(selectedFile).finally(() => {
if (loadingFilePathRef.current === loadingPath) {
loadingFilePathRef.current = null;
}
});
}, [loadSelectedFile, loadedFilePath, selectedFile]);
// Sync isDirty to a ref so the polling interval can read the latest value
@@ -2192,7 +2198,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
const getFileStatus = React.useCallback((path: string): FileStatus | null => {
// Check open status
if (openPaths.includes(path)) return 'open';
// Check git status
if (gitStatus?.files) {
const relative = path.startsWith(root + '/') ? path.slice(root.length + 1) : path;
@@ -2210,7 +2216,7 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
if (!gitStatus?.files) return null;
const relativeDir = dirPath.startsWith(root + '/') ? dirPath.slice(root.length + 1) : dirPath;
const prefix = relativeDir ? `${relativeDir}/` : '';
let modified = 0, added = 0;
for (const f of gitStatus.files) {
if (f.path.startsWith(prefix)) {
@@ -615,9 +615,8 @@ export const TerminalView: React.FC<TerminalViewProps> = ({ visible }) => {
const handleAttachSelection = React.useCallback(() => {
const selection = terminalControllerRef.current?.getSelection();
const sessionKey = currentSessionId ?? (newSessionDraft?.open ? 'draft' : null);
if (!selection || !sessionKey || !activeTab) return;
addContextDraft({
sessionKey,
if (!selection || !sessionKey || !activeTab || !effectiveDirectory) return;
addContextDraft({ directory: effectiveDirectory, sessionKey }, {
source: 'terminal',
fileLabel: activeTab.label,
startLine: selection.startLine,
@@ -626,7 +625,7 @@ export const TerminalView: React.FC<TerminalViewProps> = ({ visible }) => {
language: activeTab.terminalSessionId ?? activeTab.id,
text: '',
});
}, [activeTab, addContextDraft, currentSessionId, newSessionDraft?.open]);
}, [activeTab, addContextDraft, currentSessionId, effectiveDirectory, newSessionDraft?.open]);
const handleSelectTab = React.useCallback(
(tabId: string) => {
@@ -382,8 +382,8 @@ export const PullRequestSection: React.FC<{
const canShow = Boolean(directory && branch && baseBranch && (branch !== baseBranch || isFork));
const prStatusKey = React.useMemo(
() => getGitHubPrStatusKey(directory, branch),
[directory, branch],
() => getGitHubPrStatusKey(directory, branch, selectedRemote?.name ?? null),
[directory, branch, selectedRemote?.name],
);
const statusEntry = useGitHubPrStatusStore((state) => state.entries[prStatusKey]);