Skip to content

[fix] Release the scroll guard when a transcript node swap cancels a pin - #5605

Merged
mmabrouk merged 1 commit into
fe-chore/agent-conversation-splitfrom
fix/transcript-scroll-guard
Jul 31, 2026
Merged

[fix] Release the scroll guard when a transcript node swap cancels a pin#5605
mmabrouk merged 1 commit into
fe-chore/agent-conversation-splitfrom
fix/transcript-scroll-guard

Conversation

@mmabrouk

Copy link
Copy Markdown
Member

Fix for a finding on #5569. Targets fe-chore/agent-conversation-split so it lands as part of that PR.

Context

When the plain transcript container is replaced while a pin animation is still running, the chat stops following the streaming answer and message anchoring stops compensating for content that grows above the viewport. It stays broken until some later pin happens to finish on its own.

animatePinTo holds programmaticScrollRef true for the length of the glide, so the scroll handler and the resize observer ignore the in-between frames. It releases the guard inside the settle callback. The cancel path stored on pinCleanupRef removes the listeners and clears the fallback timer, but it never touches the guard, and cancelling skips settle by design. The node-swap layout effect calls that cancel path, so the guard leaks as true.

It leaks past the remount because programmaticScrollRef comes from intent, which the parent owns. The ref outlives this hook, so the stuck value is still there when the plain engine mounts again.

Three consumers bail out while the guard is set: the follow-to-bottom effect, the resize anchoring effect, and the scroll handler's anchoring branch.

The container is conditionally rendered (AgentTranscript.tsx:43 and :101), so it swaps whenever virtualization takes over from an empty conversation, and whenever the transcript remounts within a session.

Changes

Release the guard at the two places that cancel a pin for teardown: the node-swap layout effect and the unmount cleanup.

The guard is deliberately not released inside the shared cancel closure. animatePinTo calls that same closure to supersede a previous pin, and every caller sets the guard to true immediately before calling it, so clearing there would wipe the guard that was just set.

Ordering is safe. The node-swap effect is declared before the SC-1/SC-2 pin effect, and React runs layout effects in declaration order, so a pin that needs the guard sets it after this clears it.

Notes

I could not run pnpm lint-fix, because the checkout I used has no node_modules. The change is two statements plus a comment and follows the surrounding style.

What to QA

  • With virtualization on, open a fresh agent chat with no messages and send one. The transcript follows the streaming answer to the bottom.
  • Switch to another session mid-answer, then come back. Sending a new message still follows.
  • Scroll up while an answer streams. The jump-to-latest pill appears, and clicking it glides to the bottom and resumes following.
  • Regression: park the view partway up a long conversation while an answer streams below. The view should stay where you put it, and the message you are reading should not drift as content above it renders.

@dosubot dosubot Bot added the size:XS This PR changes 0-9 lines, ignoring generated files. label Jul 31, 2026
@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview Jul 31, 2026 5:08pm

Request Review

@dosubot dosubot Bot added bug report Something isn't working frontend labels Jul 31, 2026
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 534d091f-a019-4f0d-b684-4a1f05594a6f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Status Destroyed (PR closed)

Updated at 2026-07-31T19:18:23.555Z

@mmabrouk
mmabrouk merged commit 5f2d450 into fe-chore/agent-conversation-split Jul 31, 2026
39 of 40 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug report Something isn't working frontend size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant