Repository navigation
fix(app): keep loading history through long turns - #1842
Merged
Merged
Conversation
Scrolling up a long chat could stop for good partway through one turn. The chat list stops paging after five pages that bring nothing new into view and waits for Load more. It judged "new" by the id of the oldest row, but a collapsed work group keeps its id while the older part of its turn arrives, so every page of a long turn counted as empty: a turn of more than about five hundred records paused before the prompt that opened it came into view. The oldest group's earliest member is now part of that identity, so a page that extends the turn counts as progress. Pages of rows that never render, and live rows at the newest end, still count against the budget as before. Getting past the pause was also harder than it looked on a phone. The footer placed the spinner / Retry / Load more slot first and the header spacer second, on the belief that the footer is drawn bottom-to-top. FlashList counter-flips the footer as a whole, so it reads top-to-bottom like any view, and the slot landed at the very top of the content: on a phone, behind the header and the status bar, where the one control that resumes paging can be neither seen nor tapped. The spacer now comes first, so the slot sits against the oldest row. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
Contributor
|
Merging, thank you :D |
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.
Scrolling up a long chat can stop for good partway through one agent turn. With Group Tool Calls on, a turn longer than about five 100-record pages pauses automatic history paging before the prompt that opened it is reached, and on a phone the only way past that pause — Load more, or Retry after a failed page — is drawn at the top edge of the scrolled content, behind the glass header and the status bar. Expected: the list keeps paging through a long turn until its prompt arrives, as the existing test
pages automatically near the oldest row, one page at a time, until the turn completesdescribes, and Load more / Retry sit right above the oldest row.Why it stops
fillOlderpauses afterMAX_INVISIBLE_OLDER_PAGESpages that leave the oldest row's id unchanged. A collapsed work group is keyed by the message its run completes at (work-<final answer id>, seecollectAgentWorkGroups) precisely so that it keeps its identity while older work arrives — so every page that lands inside the oldest turn looks empty. The existing test pages only two pages into a turn, which stays under the budget.OlderEndrenders the status slot first and the spacer second because, per its comment, "the two children are in visual order bottom-to-top … each cell is counter-flipped". FlashList counter-flips the footer as a whole (renderFooterinrecyclerview/hooks/useSecondaryProps.tsxwraps it in the same inverted transform), so its children read top-to-bottom like any view and the slot ends up at the far top of the content. On a phone,SessionViewruns the list under the header (chatListTopContentInset,headerOverlayHeight), so at the top of a long chat the slot is behind the header and the status bar.Changes
oldestBoundary()— the far end's identity now includes the oldest collapsed group's earliest member, so a page that extends the oldest turn counts as progress. Pages of rows that never render, live rows at the newest end, and expanding a group still leave it unchanged; the existing budget tests pass as they are.OlderEnd— spacer first, status slot last, so the spinner / Retry / Load more sit against the oldest row. The comment now describes what FlashList does.mainit stops at five with Load more).Proof
Standalone server + Expo web, one seeded session whose middle turn has 700 tool calls (1,406 stored records), Group Tool Calls on. On
mainthe list makes 11 older-history requests — the first page, five background pages, five of its own — then stops at Load more. With this change it makes all 15 and reaches the first message.With older-history requests delayed by 3 s, the spinner sits 94 px above the oldest row on
mainand 6 px above it with this change:The phone-only consequence — the slot hidden behind the glass header — was not captured on a device; it follows from the position measured above and the phone layout in
SessionView.tsx.pnpm typecheckis clean andvitest runpasses 1,919 tests.sessionPresentation.test.tsfails to load (__DEV__ is not defined, from expo-modules-core) with or without this change.Related: #1814 reported the same scenario — history stopping inside a long tool-only turn — against the paging code before
ca9257a30rewrote it.🤖 Generated with Claude Code