Repository navigation
Fix false timeline catch-up indicators and scroll shifts - #5068
Merged
Merged
Conversation
ymichael
added a commit
that referenced
this pull request
Oct 7, 2026
## Human comments ## What was wrong #5068 fixed when "Loading latest messages…" shows and hides, but it also moved the indicator. The row at the end of the timeline became a sticky pill, fixed 8px below the top of the scroll area against the right edge of the reading column. Because it floated over the column with no reserved space, it covered message text in the middle of the pane. The move was unintentional. ## What changed - `ThreadTimelineSurface.tsx`: removes the sticky overlay wrapper. The indicator is again a shimmering `TimelineStatusIndicator` row inside a `HeightTransition`, after the timeline rows and before the working indicator. - #5068's show/hide behavior is unchanged. The flag is still based on comparing event sequences. The row appears only after the timeline has been behind for one second and disappears as soon as it catches up. The timer restarts when you switch threads or a new catch-up begins. - `docs/timeline-pagination.md`: the catch-up paragraph now describes a row at the end of the timeline instead of an overlay. Tradeoff: since it's a row again, removing it can shift the messages, which #5068 avoided. The one-second delay means this only happens on slow catch-ups. ## How you verified - `pnpm exec turbo run typecheck lint --filter=@bb/app` passed; the existing lint warnings are unchanged. - `ThreadTimelineSurface.test.tsx` (4 tests, including the reveal delay, immediate hide, and per-thread timer reset) passed through Turbo. - In the `thread/timeline/Catch-up indicator` Ladle story with "Catching up" on, the row renders under the last reply, above the composer and aligned with the message text. It no longer overlaps the conversation. > AGENT GENERATED 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.
Human comments
What was wrong
Opening an unread thread could show “Loading latest messages…” even when its cached timeline already included the notified events. Catch-up state tracked whether an event arrived while the timeline was inactive, rather than comparing the notification with the loaded snapshot. Delayed/coalesced notifications could therefore mark current data as behind. The loading row also remained visible for at least 320 ms and occupied timeline space; collapsing it moved visible messages by 44 px in the browser reproduction.
What changed
metadata.timelineSequencein event-append notifications and preserve the highest sequence through server coalescing and client debouncing.maxSeq. A response acknowledges only events through its own sequence, so an older response cannot clear a newer pending change. Notifications without a sequence still refresh the cache without claiming that messages are missing.docs/timeline-pagination.md.The additive notification field is on the server-to-client realtime contract. Server/host-daemon wire payloads are unchanged, so no daemon protocol bump is needed. Existing CLI/SDK timeline and notification surfaces carry the change without new commands or settings.
How you verified
The initial sequence/layout verification passed 274 focused tests: app cache/controller/surface (116), server hub/event writes/ingestion (54), database events (96), and domain notification contracts (8). Ran through Turbo with the affected test-file filters.
pnpm exec turbo run lint typecheck --filter=@bb/app --filter=@bb/server --filter=@bb/db --filter=@bb/domainpassed; existing app lint warnings remain.git diff --checkpassed.Regression tests failed against the old implementation for already-covered notifications, premature stale-state clearing, and the indicator remaining visible after catch-up.
Built and launched
pnpm start:worktree; verified healthy server/daemon and created two real provider conversations through the branch-built BB CLI. A cached sequence-39 timeline showed loading while its actual sequence-52 response was held, then displayed the reply and removed loading in the same observed DOM update.Observed a real coalesced sequence-60 notification arrive after sequence 64 was loaded. Reopening with the refresh held for 650 ms showed no loading indicator. Cold-load skeleton, reload persistence, CLI event pagination, and read-state checks also passed.
Desktop Chromium measurements with unchanged messages: indicator removal moved the last message 44 px before, 0 px after, repeated three times. Native/mobile engines were not tested.
Follow-up reveal-delay verification: all four timeline-surface tests plus app lint/typecheck passed. Tests cover a 900 ms catch-up staying silent, the 999/1000 ms reveal boundary, immediate hiding, and timer resets. Rebuilt the worktree server and observed first visibility at approximately 1.05 seconds, unchanged message position, and hiding approximately 7 ms after releasing the real response.