- App is running from this repository.
- A thread exists with more than 50 messages (send many short messages, or use a long-running session).
- Open a thread with 60+ messages.
- Observe that the conversation list does not show all messages immediately — only the most recent ~50 are rendered.
- Verify the latest messages are visible and the chat is scrolled to the bottom.
- Confirm a "Load earlier messages" button appears at the top of the visible list.
- Scroll up slowly toward the top of the conversation list.
- When the scroll position reaches within ~200 px of the top, verify that the previous 30 messages appear automatically above the current ones.
- Confirm the viewport does not jump — the messages you were reading stay in view.
- Repeat scrolling up to verify additional chunks load on demand.
- Once all messages are loaded, verify the "Load earlier messages" button disappears.
- Reload the page and open the same long thread.
- Click "Load earlier messages" button without scrolling.
- Verify 30 older messages are prepended and scroll position is preserved.
- Start an active Codex session (or send many messages in quick succession).
- Let the conversation exceed 50 messages while staying scrolled to the bottom.
- Verify the rendered count stays bounded (top of the DOM list advances as new messages arrive).
- Scroll up and confirm "Load earlier messages" works to reveal trimmed messages.
- In a thread with a turn that can be rolled back, trigger a rollback.
- Verify the conversation does not go blank — messages still render after the list shrinks.
- Confirm
renderWindowStartrecovers gracefully and earlier messages remain accessible.
- Only ≤50 messages are in the DOM on initial load.
- Scrolling to the top (or clicking the button) appends older messages without a viewport jump.
- During live output, the rendered window stays bounded; old messages are trimmed from the top while the user follows the bottom.
- After a rollback the conversation remains visible; no blank screen.
- No persistent state is changed — closing or refreshing the tab resets the render window.