anomalyco / anomalyco/opencode
perf(tui): make session switching independent of transcript length
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Problem
Switching between pinned sessions with Ctrl-1/2/3 has a noticeable pause, especially once the sessions have long transcripts. That undermines the quick-switch flow: the shortcut changes the route immediately, but the destination is not immediately usable.
This is a performance follow-up to #36193, not another session-navigation proposal.
What the V2 TUI does today
Checked against origin/v2 at 27e7b0558a:
session.quick_switch.Nresolves the pinned slot and immediately navigates to the destination session (packages/tui/src/context/local.tsx).- On every session ID change,
createSessionRowssynchronously reduces the destination's cached transcript into render rows, then reconciles those rows (packages/tui/src/routes/session/rows.ts). - Session hydration requests the newest 200 messages in one page and keeps them in the shared data store (
packages/tui/src/context/data.tsx). A message can expand into many rendered rows because assistant content/tool parts are embedded in it. - The session view mounts a
SessionRowViewfor every reduced row in the loaded transcript (packages/tui/src/routes/session/index.tsx). There is no viewport window or incremental transcript mounting on the switch path. - The message API itself already supports bounded, cursor-based pagination (
packages/server/src/handlers/message.tsandpackages/core/src/session.ts), so the missing piece is mainly TUI-side loading/rendering policy rather than a new storage primitive.
This explains why repeatedly switching among already-known sessions can still lag: route lookup is cheap, but transcript reduction/reconciliation/layout work scales with the loaded transcript and its parts.
Desired behavior
Quick-switch latency should be effectively independent of total session length. Switching to an already-open/pinned session should paint the destination chrome and newest visible transcript promptly, while older history loads or mounts only when needed.
Scope
- Measure the switch path separately: route navigation, transcript fetch/cache lookup, row reduction/reconciliation, and terminal layout/render.
- Bound work performed before the first destination frame (for example, a recent/visible window rather than all 200 loaded messages and all nested parts).
- Use the existing cursor pagination to load older history when the user scrolls upward, or otherwise virtualize/window mounted transcript rows.
- Preserve live updates, sticky-bottom behavior, message navigation, revert boundaries, and full transcript export.
- Add a representative long-session performance regression check or timing fixture so this does not drift back.
Acceptance criteria
- Back-to-back
Ctrl-1/2/3switches among long pinned sessions no longer have transcript-length-dependent pauses. - The destination's header/composer and newest visible messages appear before older history is fetched or mounted.
- Scrolling upward can still reach the complete transcript without losing ordering or live event updates.
- The issue includes before/after timings from a repeatable long-session fixture.
Source discussion: https://slack.com/archives/C0BFMFJ5FJS/p1785255726318509
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 by measuring the switch path through packages/tui/src/context/local.tsx, packages/tui/src/routes/session/rows.ts, packages/tui/src/context/data.tsx, and packages/tui/src/routes/session/index.tsx. Read the existing pagination in packages/server/src/handlers/message.ts and packages/core/src/session.ts, then establish a repeatable long-session timing fixture. Done means quick switching shows the destination promptly, older history remains reachable, live updates and navigation are preserved, and before/after timings are recorded.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cli, performance
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100