fix(observer): align scroll-anchor ids with transcript display-block keys#1849
Merged
Conversation
…keys The outer AgentSessionThreadPanel fed useAnchoredScroll raw seq:timestamp ids while the inner transcript rendered aggregated block keys (turn:<id>, session-boundary:..., item ids). Disjoint namespaces caused the floor writers to fire on every raw event against a DOM that barely changed, producing flicker and jump-to-tail during mid-history reading. Derive the same display-block keys in the outer panel via buildTranscriptState → buildTranscriptDisplayBlocks → getDisplayBlockKey, stabilize the string[] by value with useStableArrayShallow so the hook's messages reference only changes when the block id sequence actually changes, and fold the view mode into the hook reset key so toggling raw ↔ transcript re-initializes cleanly. Fixes the v0.4.2 regression introduced by #1825.
…level tests
The original tests used pure derivation assertions that would stay green
if useStableArrayShallow were removed, mode were dropped from the reset
key, or data-message-id rendering stopped entirely. Replace them with
behavior-level coverage that exercises the actual production wiring:
- Reference stability: verify useStableArrayShallow preserves the {id}[]
reference when ordered block ids are value-equal, and propagates a new
reference when they change (revert-proven: bypassing stabilization
fails test 7).
- Hook-level zero-write: render useAnchoredScroll with an instrumented
scrollTo spy; same ids at-bottom → zero writes, same ids mid-history →
zero writes, new id at-bottom → one floor write, new id mid-history →
no floor write.
- Ordered DOM parity: derive block keys and compare ordered sequences per
commit including the transient [turn, single] → [single, turn] reorder.
- Mode-toggle reset: exercise channelId changes (transcript ↔ raw) in
loaded and connecting→loaded states, assert re-pin behavior.
… events D2: Extract the derivation chain (buildTranscriptState → buildTranscriptDisplayBlocks → getDisplayBlockKey) into an exported deriveTranscriptBlockIds helper in agentSessionTranscriptGrouping.ts. AgentSessionThreadPanel now calls this helper — tests exercise the same production code path from raw ObserverEvents instead of pre-built TranscriptItems. D1: AgentSessionTranscriptList cannot render under node:test — the component tree transitively imports .css (BuzzLogoAnimation.tsx → buzz-logo-animation.css) and the test-loader has no CSS stub. Named residual documented in test file; the structural guarantee (both sides call the same getDisplayBlockKey) is tested, but full-component render coverage requires a test-loader change.
…then tests Fix the same disjoint-namespace scroll-id bug in AgentSessionTranscriptList's autoTail path (profile live-activity cards) that was already fixed in the thread panel: derive block keys from displayBlocks and feed value-stabilized ids to useAnchoredScroll. Skip deriveTranscriptBlockIds when showRawFeed is true (unnecessary work). Strengthen the mid-history zero-write test to actually latch mid-history via onScroll. Widen buildTranscriptState to readonly ObserverEvent[] and drop the cast in deriveTranscriptBlockIds.
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.
Summary
The observer feed's
useAnchoredScrollhook was fed rawseq:timestampids while the innerAgentSessionTranscriptListrendered aggregated transcript block keys (turn:<id>,session-boundary:..., item ids). This id-namespace divergence caused the hook's floor writers to fire on every raw event against a DOM that barely changed, producing flicker and jump-to-tail during mid-history reading.This PR fixes the divergence in both surfaces that wire
useAnchoredScrollto observer transcript blocks:Thread panel (
AgentSessionThreadPanel) — derives display-block keys from raw events via the production chain (buildTranscriptState→buildTranscriptDisplayBlocks→getDisplayBlockKey), stabilized by value so themessagesreference only changes when the ordered block-id sequence changes. View mode (rawvstranscript) folded into the reset key for clean re-initialization on toggle.autoTail consumers (
AgentSessionTranscriptList, used by profile live-activity cards) — the same disjoint-namespace bug existed here: the hook was fedTranscriptItemids while the DOM renderedgetDisplayBlockKey(block). Fixed by deriving block keys from the already-computeddisplayBlocksarray, value-stabilized withuseStableArrayShallow. Non-autoTail behavior (hook disabled viachannelId: null) is unchanged.Additional cleanup: raw-mode derivation skip (don't run
deriveTranscriptBlockIdswhenshowRawFeedis true),buildTranscriptStatewidened toreadonly ObserverEvent[](eliminating the cast inderiveTranscriptBlockIds), and the mid-history zero-write test strengthened to actually latch mid-history viaonScroll.useAnchoredScroll.tsis byte-zero — untouched.Changes
agentSessionTranscriptGrouping.ts— exportderiveTranscriptBlockIdshelper (production derivation chain); dropas ObserverEvent[]cast now thatbuildTranscriptStateacceptsreadonly.agentSessionTranscript.ts— widenbuildTranscriptStateandbuildTranscriptto acceptreadonly ObserverEvent[].AgentSessionTranscriptList.tsx— derive block keys fromdisplayBlocksviagetDisplayBlockKey, stabilize withuseStableArrayShallow, feed touseAnchoredScrollinstead of rawitems. Only active whenautoTailis true.AgentSessionThreadPanel.tsx— skipderiveTranscriptBlockIdswhenshowRawFeedis true; derive transcript block ids, stabilize, fold mode into reset key.useAnchoredScroll.observerScrollIds.test.mjs— mid-history test now latches mid-history via doubleonScroll(clear settle guard + latch anchor) with a fake[data-message-id]row, matching the pattern in the "new id mid-history" test.What this fixes
Fixes the v0.4.2 regression introduced by #1825.