Skip to content

feat(desktop): preserve transcript continuity across bounded ranges #4123

Description

@Astro-Han

Problem

#4206 replaced Renderer turn virtualization with a bounded Runtime Host range. That change substantially improved long-transcript performance and removed a second scroll and layout authority.

One problem remains. When the resident range does not reach the transcript's real edges, the reader is given no marker where the unloaded history is. TranscriptHistoryNotice (packages/ui/src/chat-view.tsx) is a banner at the top of the section, not a row at the position of the break — so a live overlay Turn appended after an older durable window reads as continuous with it, and there is nothing at the break to navigate from.

Correction (2026-09-04)

Two earlier statements in this issue were wrong, and one of them shaped an implementation that is larger than the problem. Both are now removed from the text above and below; recording them here so the change is auditable.

"This can make separated ranges appear adjacent." The resident durable set is always a single contiguous window. #evictToBudget (apps/desktop/src/main/desktop-transcript-replica.ts) only advances the oldest index and retreats the newest index — it never removes from the middle — and #acceptRange throws on an internal sequence gap while durable coverage is complete. Two non-adjacent durable ranges cannot be resident at once.

"Desktop no longer has enough position information to represent them." Everything needed to place and label a break already crosses the preload boundary: each durable fragment carries its identity (durable sequence), and batches carry durableThrough, hasOlder and hasNewer; loadBefore(anchorSequence) and loadAround(sequence) are the existing entry points. From these the renderer can already derive how many messages are missing at each edge, where the break is, and which sequence to request. The one thing it cannot derive is how many Turns a break contains, because message count is not Turn count.

The former Direction section then prescribed an implementation — maintain Turn positions incrementally, page them with a keyset cursor and a revision or watermark — and the third acceptance criterion presupposed a position snapshot exists. That is a solution written into an issue, and it is the reason #4560 spans Storage schema, a new pager, a signed cursor and a protocol epoch bump. The direction below states the outcome instead.

Current boundary

flowchart LR
    S[Storage<br/>complete transcript] --> P[Runtime Host pager<br/>bounded bodies]
    P --> D[Desktop<br/>bounded projection]
    D --> R[Renderer<br/>single scroll authority]
Loading

Direction

Represent a break in the transcript where it actually is, using what already crosses the boundary:

  • Project the transcript as rows, where a row is either a Turn or a gap, and place the gap at the break rather than above the transcript.
  • Let a gap be actionable: loading from it goes through the existing loadBefore / loadAround / latest-range entry points.
  • Continue loading message bodies through the existing session.transcript.page path.
  • Keep the transcript scroll authority the only writer of scroll position; a gap row must not introduce a second one.
  • Deliver the first change as an end-to-end Desktop slice.

A per-Turn position index is not part of this. It buys one thing — labelling a gap in Turns rather than in messages — and that is not worth a Storage table, a second pager, a cursor format and a protocol epoch. If a later consumer needs Turn counts, query the index on demand; do not page it to the client. Do not introduce materialized full-history snapshots, durable generations or leases, a second body protocol, or a custom byte scanner.

Acceptance

  • A break between the resident range and unloaded history is marked at its own position, not as a banner above the transcript.
  • Navigating tail to old Turn to intermediate Turn to tail never presents unloaded history as continuous.
  • Loading from a gap uses the existing range entry points and needs no new protocol operation.
  • The existing transcript scroll authority remains the only writer of scroll position.
  • Large-transcript performance does not regress from perf(desktop): bound transcript rendering and eliminate scroll LoAF #4206.

Related: #4206, #4244, #4295. Implementation attempt: #4560.

Codex helped analyze the architecture and draft the original correction. The 2026-09-04 correction above was verified against the current implementation.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions