acpmux: open long chats from the end of their log, with a size cap (cx-2lv1k)

Merged
TE
22d ago
+614-16

Opening a long chat no longer reads its whole event log. A tail page, or an older page while scrolling up, now reads only the segments it needs, and a new maxBytes caps how much one page sends, so a remote session's open stays small however long or heavy the chat is.

What changes

  • Pages from the end read from the end.
    • Before, _acpmux/attach and _acpmux/events with beforeSeq parsed every segment from the first one while keeping a ring of the last N records, so opening a chat cost as much as reading all of it. On cmux server that cost is paid on every open.
    • Store::scan_back now walks segments newest first. It skips a segment whose first record is already at or past the cursor, after reading one line, and parses lines in reverse until the page is full. The in-memory store gets the same order from its existing scan.
  • maxBytes on attach and events. A page ends once its records' JSON reaches the cap, always holding at least one record, and reports hasMore. Without the parameter, pages are unchanged. The pane starts sending it in #20536.
  • Unchanged: hasMore semantics, filters and forward paging. The existing paging test in transcript_events.rs covers them.

Evidence

  • tail_pages_read_only_the_newest_segments: a session spread over many small segments still returns the right newest page and the page before it when its oldest segment can't be read. Red on 8735797 (hosted Linux): the newest-N scan reads that segment and fails.
  • opening_a_long_chat_stays_within_its_payload_budget: a 5000-turn chat with 4 KB tool outputs, opened the way the pane opens it. The serialized attach reply, which is what a remote client downloads, must be at most 160 KiB, end on the latest record and say hasMore. Red on 2a28fb2, where attach ignores maxBytes.
  • Green: the transcript_events:: module on hosted Linux and macOS at the head, with results in the PR checks.

Measured

history_page_cost (an ignored measurement in the same file): 3000 turns, 183,006 records, 60.6 MiB in 8 MiB segments. Debug build on the fleet, before (bcf6bb7) and after (e02fe59):

beforeafter
Open (attach, newest 400)218 ms3โ€“4 ms
Older page from mid-history109 ms9 ms
Older page near the start13 ms18 ms
Whole log, for reference170 ms179 ms

A page near the start reads every later segment's first line, then the segments it needs, so it costs about what it did. The fleet's Rust step can't pass --ignored, so both runs used a measurement-only copy that writes its numbers to a file, branch op-next-replay/history-cost-measure; this PR doesn't include it.

Changelog

  • Fixed: Long agent chats open quickly instead of reading their whole history first, which mattered most for remote sessions.

๐Ÿค– Generated with Claude Code