fix: resolve performance regression in transcript loading and session search#55
Conversation
Two performance regressions introduced in recent PRs: 1. readAgentConfigs blocked transcript rendering (PR matt1398#50) The agent config IPC call was awaited on the critical path of fetchSessionDetail, preventing any transcript data from rendering until the filesystem read completed. On macOS this was especially noticeable due to security checks on first directory access. Fixed by making the call fire-and-forget: the transcript renders immediately and subagent color badges update asynchronously. Also set the project cache key optimistically before the async call to prevent duplicate in-flight requests on rapid navigation. 2. SessionSearcher stat()-called every session file on each search (PR matt1398#53) LocalFileSystemProvider.readdir() did not populate the optional mtimeMs field on FsDirent entries. The new SearchTextCache-based SessionSearcher fell back to an individual fsProvider.stat() call per session file when mtimeMs was missing, adding N extra filesystem round-trips on every search in local mode. Fixed by statting all entries concurrently inside readdir(), so mtimeMs is always populated and the stat fallback is never triggered. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Closing to re-submit after running full quality gates (typecheck, lint, test, build). |
|
Quality gates verified before re-opening: |
|
@proxikal The performance issue you're seeing is likely not caused by the PRs itself. There's a known problem where upgrading from one version to another can leave stale references behind (possibly in ~/Library/Application Support/ or ~/Library/Caches/ on macOS) that cause a significant lag spike. I experienced the same thing upgrading from 0.4.2 to 0.4.3 and 0.4.3 to 0.4.4 for example. This points to a version upgrade/migration issue rather than a problem with the code as implemented. For reference, I did a clean install (both downloading the DMG directly and installing via Homebrew) and it worked perfectly with no lag. @matt1398 FYI. |
|
Thank you @cesarafonseca for the information. I'm going to figure out how to fix that locally so i can have a smooth migration experience, i think the best way (personally) would be a version-stamp check on startup before the window opens, compare the stored version against the current app.getVersion(). If they differ, wipe the Code Cache and GPUCache directories and then Electron recreates them automatically, then update the stamp. A little hackey though. |
Summary
This PR fixes two performance regressions introduced in PRs #50 and #53 that caused noticeable lag when opening transcripts and performing session searches in local mode.
Problem 1 — Transcript loading blocked by
readAgentConfigs(introduced in #50)Symptom: Clicking a transcript would hang with a loading spinner for a noticeable delay before anything rendered. This was most pronounced on macOS due to filesystem security checks on first directory access.
Root cause:
fetchSessionDetailinsessionDetailSlice.tsawaitedapi.readAgentConfigs(projectRoot)on the critical render path — nothing could render until the IPC round-trip to read.claude/agents/completed. Additionally, the project-level cache used a single module-level string, so switching between two projects triggered a new filesystem read on every switch.Fix: Made the call fire-and-forget. The transcript now renders immediately; subagent type color badges update asynchronously once configs arrive. The cache key is set optimistically before the async call to prevent duplicate in-flight requests during rapid navigation.
Problem 2 — Redundant
stat()per session file on every search (introduced in #53)Symptom: Session search felt slower than expected in local mode.
Root cause:
SearchTextCacherequiresmtimeMsper session file for cache invalidation.LocalFileSystemProvider.readdir()returnedFsDirententries withoutmtimeMs(Node'sreaddirwithwithFileTypesdoesn't include stat info). The newSessionSearcher.searchSessions()has a fallback —await fsProvider.stat(filePath)— which triggered for every session file on every search in local mode, adding N extra filesystem round-trips.Fix:
LocalFileSystemProvider.readdir()now stats all entries concurrently during the readdir call, populatingmtimeMson every returned entry. The stat fallback inSessionSearcheris never triggered for local mode.Files Changed
src/renderer/store/slices/sessionDetailSlice.tsreadAgentConfigsfire-and-forget with optimistic cache keysrc/main/services/infrastructure/LocalFileSystemProvider.tsmtimeMsinreaddir()via concurrent stat callsTest Plan
🤖 Generated with Claude Code