[Bug]: server.getUsageSummary scans an unbounded project tree and blocks macOS for 50+ seconds
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 23k
- Forks
- 5.9k
- Avg merge
- 11h 14m
- Merged PRs (30d)
- 357
Description
Summary
On macOS, opening the Usage page can block on server.getUsageSummary for roughly one minute. The slowdown is caused by the local transcript scan, not by the Mac hardware or by Claude actively running.
The issue is especially reproducible when Claude has not been used for months and ~/.claude/projects does not exist: the current resolver falls back to ~/projects, then recursively walks that entire tree looking for .jsonl files.
Reproduction
- Use T3 Code on macOS with no
~/.claude/projectsdirectory (for example, Claude Code has not been used for months). - Have a normal Codex history under
~/.codex/sessionsand a non-trivial~/Projectstree containing build artifacts and unrelated.jsonlfiles. - Open the Usage page, or request
server.getUsageSummary. - Observe that the request remains pending for 50+ seconds.
Expected behavior
- A missing default Claude transcript directory should be reported as missing and skipped.
- The usage scanner should only walk explicitly configured transcript roots, not an entire home-level project directory as an implicit fallback.
- Usage should load within a bounded, predictable time and expose per-provider scan progress/duration if a large cold scan is unavoidable.
Actual behavior
In the installed nightly bundle, UsageService.resolveTranscriptDirs resolves the default Claude home to the user's home directory, checks <home>/.claude/projects, and returns <home>/projects when that directory does not exist. listTranscriptFiles then recursively calls readdir on every directory and stats every .jsonl file without excluding build directories or other unrelated trees.
This means an inactive/missing Claude installation can make the Usage request scan the complete local project tree.
The same request also scans Codex rollouts. On this machine:
~/.codex/sessions: 1,249 JSONL files, about 5.33 GiB total.- Cached 90-day scan: 910 Codex files, about 4.14 GiB.
- The persisted usage cache is already present and only about 6.4 MB, so cache serialization is not the bottleneck.
- The cache contains 75 stale/ unrelated files classified as Claude transcripts under
~/projects, including simulator logarchive and ASR benchmark JSONL files.
Trace evidence
Environment: b5a216d4-8818-4c48-9585-cd810786edc2
T3 Code Nightly: 0.0.34-nightly.20260815.1101 (cf7bfd1c9397)
Platform: macOS 27.0, Apple Silicon
Trace ID: 1440c415efea5a815352529ecdbefd64
Observed spans from ~/.t3/userdata/logs/server.trace.ndjson:
ws.rpc.server.getUsageSummary: 54,137.8 msUsageService.readSummary: 54,137.6 ms- A second consecutive call: 56,908.5 ms
UsageService.ensureRates: 10.3 msUsageService.resolveTranscriptDirs: 1.7 msUsageService.persistScanCache: 20.8 ms
The uninstrumented gap is therefore inside the directory walk and transcript parsing loop, not rate lookup or cache persistence.
Suggested fix
- Do not fall back from a missing default
~/.claude/projectsto~/projects. Treat the default Claude root as missing unless a custom Claude home was explicitly configured. - Resolve all enabled provider instances' transcript roots explicitly and deduplicate them.
- Skip known non-transcript directories and/or maintain a directory/file index instead of recursively walking broad roots on every request.
- Add spans/counters around directory walking, file reads, and per-provider scan totals so future slow scans identify the exact root and phase.
- Consider serving the last cached summary immediately and refreshing a cold scan in the background.
Related, but distinct, Usage issues: #5798 and #5805.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at server.getUsageSummary and trace UsageService.readSummary through resolveTranscriptDirs and listTranscriptFiles. Reproduce the missing ~/.claude/projects case, then inspect the trace spans around directory walking and transcript parsing. Done means the missing default root is skipped instead of falling back to ~/projects, scans stay bounded to explicit roots, and scan progress or duration is exposed where applicable.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend, observability, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100