Skip to content

fix(ui): hear the reader when resize lands before scroll - #4809

Draft
Astro-Han wants to merge 1 commit into
mainfrom
cursor/transcript-pin-resize-race-6558
Draft

fix(ui): hear the reader when resize lands before scroll#4809
Astro-Han wants to merge 1 commit into
mainfrom
cursor/transcript-pin-resize-race-6558

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

Main's Storybook smoke has been red on about half the runs that actually execute it since #4766 landed. Two AppShell stories take turns failing; the product defect underneath is in TranscriptScrollAuthority, not in those play functions.

ResizeObserver wrote scrollTop to the tail whenever the pin was still on. Pin is only updated on scroll events. The two deliveries are unordered. A reader who has already moved the offset — typical when content-visibility: auto resolves estimated Turns they just uncovered — gets yanked back if the layout callback wins. That is a normal user path, not a test-only race, and it is also ReaderScrolledUpIsNotPulledBack assigning scrollTop then waiting six frames.

The classifier that already distinguishes "content moved the offset" from "the reader did" on scroll events now runs on resize too. It is not the write-echo check: growth under a pin leaves scrollTop on lastWrittenTop until the follow write happens, so treating that as an echo would stop following.

This PR does not rewrite the AppShell stories and does not change the distance ≤ 4 tail bound. Those stories still drive the scroller by assigning scrollTop and by injecting a raw box; that belongs with the coming transcript-scroll restructure. TailFollowsGrowthOutsideTurns may still flake if a scroll event mis-classifies a content-visibility correction before resize runs — that is a different, remaining hole.

Refs #4766

Verification

  • npx tsx --test src/__tests__/transcript-scroll-authority.test.ts in packages/ui: 14 passed, including the new case that failed before the change (Expected 2400 !== 1900 when resize yanked the reader back).
  • npm run format and npm run lint: clean.
  • Not run: full @maka/ui test:dist (workspace tsc currently fails on unrelated locale typing), Storybook smoke, Desktop e2e.

AI use

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Cursor Grok 4.6 diagnosed the main CI flake, wrote the failing unit test, and applied the classifier to the ResizeObserver path. Commits carry Generated-by: Cursor Grok 4.6.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

A reader who has left the tail is no longer pulled back when a layout pass arrives before the corresponding scroll event. Pinned following is unchanged: content growth still writes the tail.

ResizeObserver wrote the tail whenever the pin was still on. A reader
who had already moved scrollTop — with the scroll event still in flight —
was yanked back. That is the Storybook smoke flake on
ReaderScrolledUpIsNotPulledBack, and a real path: uncovering estimated
Turns is itself a layout pass.

The classifier that already distinguishes content from the reader on
scroll events now runs on resize too. It is not the write-echo check:
growth under a pin leaves scrollTop on lastWrittenTop until the follow
write happens.

Generated-by: Cursor Grok 4.6
@github-actions github-actions Bot added the effort/M Under 500 readable lines label Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/M Under 500 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant