Skip to content

perf(remote): bound live transcript retention for unviewed sessions #805

Description

@Juliusolsson05

Confirmed with synthetic live feed events at 5d641845 (Node v24.14.1, macOS arm64). Priority: medium for Remote users with long-lived/multiple agents. No private transcript or live app was used.

Expected and actual behavior

The phone should retain a bounded live transcript window, and should not accumulate full transcripts for every session the user has never opened. Older content should remain available through durable pagination.

TranscriptStore constructor ingests all sessions' live entries independently of view subscriptions. ingestRawEntries retains every UUID in seen, inserts tool objects into unbounded indexes, and copies/appends the entire entries array for every burst. There is no live entry count/byte window here. Session-list removal, transcript roll, and store disposal clear state, but simply not viewing a still-live session does not.

The desktop already has liveEntryWindow (2,000-entry and 32 MiB triggers with ownership constraints). The phone reuses several desktop reducers but does not apply that retention discipline.

Reproduction and measurements

Instantiate the actual TranscriptStore with a fake feed that captures the registered event callbacks, lists one synthetic Claude session, and has zero view subscriptions. Use the real Claude transcript mapper and real indexEntryIntoMaps/empty state helpers. Deliver 4,096 unique newline-equivalent assistant entries through onSessionJsonlEntries, each containing one unique Read tool call and an 8,192-character ASCII text block. Use a stable synthetic transcript filename.

The harness transpiled the store source in memory, substituted only the renderer capability registry with the actual Claude mapper factory, and left unused semantic-event modules inert. No semantic events were exercised.

Delivered entries Retained entries Retained text bytes Tool-use index Seen UUIDs View subscribers
1,024 1,024 8,388,608 1,024 1,024 0
2,048 2,048 16,777,216 2,048 2,048 0
4,096 4,096 33,554,432 4,096 4,096 0

These are retained logical payload sizes and collection cardinalities, not browser heap measurements. Tool input bodies, object/index overhead and semantic state are additional, unmeasured costs. The test stops after 4,096 entries; there is no eviction branch in this append path.

User impact and next steps

Memory grows with all received conversation/tool output while sessions remain live, even on the list screen. Phone tab reloads/termination under memory pressure and increasing append/render cost are plausible consequences; no actual mobile crash or FPS regression was measured.

  • Introduce byte- and count-bounded live retention with a smaller budget for unviewed sessions, or negotiate server interest and backfill on selection.
  • Keep entry, seen-id, tool-use/result indexes, pagination marker/offset, and semantic ownership state consistent when trimming. Never merely slice entries while leaving indexes holding the same bodies.
  • Share the desktop retention policy where practical, but explicitly adapt its ghost/history/live-turn invariants to the remote store rather than importing the entire desktop workspace.
  • Preserve paginated history and retry/reconnect semantics. Trimming must not cause duplicate rows or tool results to lose their owners.
  • Add a regression covering sustained large entries on several unviewed sessions, bounded cardinalities/bytes, later selection/backfill, and session removal.

Tradeoff: smaller retained windows increase backfill requests and may affect scroll position; no estimated percentage improvement is claimed. This is independent of the main-process socket queue bound and of B7's #795 external operator implementation.

Refs #103, #375, #475.

Related transport backlog issue: #804. A bounded send queue does not limit what a healthy receiving phone retains.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions