Repository navigation
perf(sessions): read transcripts incrementally for the picker - #740
Merged
Merged
Conversation
Refs #735 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kk16MNVJtWAnCeGxGRuHqa
The picker lists the ten most recently modified transcripts — the live sessions, whose mtime moves every few seconds — and extracted their prompts by reading, splitting and parsing the whole file, cached only by mtime. Every open therefore re-read all of them from byte 0 (122 MB on the machine this was measured on) on the main thread to show four prompts each; search did the same for every transcript on disk per query. A cache entry is now a parsed byte range: growth folds only the appended bytes, a listing folds the tail in a growing window until it has the prompts it needs, search extends to the head once and is incremental after, and the cache is an LRU of 512. Search considers the 500 most recently modified candidates. Prompt filtering, ordering and cwd resolution are unchanged. Fixes #735 Refs #103, #93, #96 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kk16MNVJtWAnCeGxGRuHqa
…nst rewrites Review follow-ups: overlapping extractions of one transcript (the picker lists on open and again from its debounced effect) both folded the same chunk into the shared cache entry and left duplicated prompts behind — calls are now chained per key; a growth read compares the last 64 bytes of the parsed range first so a rewrite that ends larger starts over; backward widening grows geometrically so a multi-MB record costs one pass; the search bound is per provider and applied after the cwd filter, with the cache sized above the bounds; need is clamped to one; the Codex head is read for a cwd at most once. Tests cover concurrency, the long-line path, mtime-only and grown rewrites, empty and partial files, a multi-byte seam, and a real LRU. Refs #735 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kk16MNVJtWAnCeGxGRuHqa
Owner
Author
|
Self-review pass (Fable adversarial review + an orchestrated Agent Code reviewer) — resolved in HEAD:
Not adopted: an age-based search bound — sessions older than the 400 most recent per provider are not searchable; stated in the code comment as the price of never freezing on a keystroke. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
src/main/sessionIndex.tsextracted a transcript's user prompts by reading the whole file, splitting it and parsing every line, cached by mtime only. The picker lists the ten most recently modified transcripts — by definition the live sessions, whose mtime moves every few seconds — so every open was a cache miss for exactly the largest files: 122 MB across the top ten on this machine (one Codex rollout alone 89 MB), read, split into millions of sliced strings and parsed on the main thread to show four prompts each. Search did the same for all 2,150 transcripts on disk per query (including a 148 MB rollout), and the module-global cache never evicted. Details in #735.What changed
[parsedFrom, parsedTo)plus the prompts folded from it, validated by mtime and size:[parsedTo, size)and folds complete lines (a partial trailing line waits for its newline);need = promptsPerSession) folds the tail in a growing window (256 KiB ×4) until it has enough prompts or reaches the head; Codex's cwd, which lives only in the headsession_meta, comes from one bounded head read;need = 'all') extends to the head once and is incremental afterwards;extractPromptsFromFileis exported with test hooks for the cache.Unchanged: per-provider prompt filters, newest-first order and
promptsPerSessionslicing, cwd resolution order, the discovery walks.Linked issues
Fixes #735. Refs #103, #93, #96. Enables #739 (smart resume).
Verification
NODE_ENV=test npx vitest run --project unit src/main/sessionIndex.prompts.test.ts— 7 tests against temp JSONL files: tail-first listing returns the newest K without parsing from the head; growth folds only appended bytes and leaves a partial trailing line for later; search extends to the head once then stays incremental; an adjacent duplicate across the tail-window seam is collapsed keeping the older copy; Codex cwd comes from the head; a shorter rewrite re-parses; missing file returns empty and the LRU evicts.npx tsc -p tsconfig.node.json --noEmit— clean.Known limitations
searchover 500 candidates is a full read per file the first time.Plan:
docs/superpowers/plans/2026-09-03-session-index-incremental-prompts.md.🤖 Generated with Claude Code
https://claude.ai/code/session_01Kk16MNVJtWAnCeGxGRuHqa