fix(signals): draft reads compose overrides over a truth-staged backing - #3266
Open
brenelz wants to merge 1 commit into
Open
fix(signals): draft reads compose overrides over a truth-staged backing#3266brenelz wants to merge 1 commit into
brenelz wants to merge 1 commit into
Conversation
With several optimistic-store actions in flight (optimistic `votes++`, server confirm, `refresh(store)`), the first vote's truth landing is staged into the transaction that still retains the second vote (solidjs#3164 fold) and that vote's increment replays over it. A third click's draft then read the staged truth WITHOUT the replayed override: draft reads composed live overrides only while `pb === null`, on the assumption that an existing pending backing was the draft's own view-seeded clone. ensurePB's hand-off parks a truth-staged backing and reseeds from the view on the draft's first WRITE, but `votes++` reads first — base 1, wrote 2, and the override it emitted landed on the value already displayed. The click was invisible and the count stuck (or fell back) until truth caught up; ten rapid votes showed 1, 2, 3, 4, then 2, then 5. Draft reads now compose overrides whenever the pending backing is not the draft's own tentative clone (tentativePBs), in all four draft paths (data key, hot path, has, ownKeys). Regression in the solidjs#2951 refetch-hold suite: three votes in flight, the third made after the first landing, every visible value monotonic. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 0212cba The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
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
With several optimistic-store actions in flight (optimistic
votes++, server confirm,refresh(store)), rapid clicks rendered 1, 2, 3, 4, then fell back to 2, then 5, while the server counted every vote. Reproduces identically at the core with a plain promise source and with an async-generator (live stream) source, so the transport is not involved.Root cause
When the first vote's truth lands while the second is still retained, the landing is staged into the retaining transaction (#3164 fold) and the second vote's
+1replays over it. A third click's draft then read the staged truth without the replayed override: the four draft-read paths (data key, hot path,has,ownKeys) composed live overrides only whiletarget.pb === null, on the assumption that an existing pending backing was the draft's own view-seeded clone. A truth-staged backing is authoritative truth without overrides.ensurePB's hand-off parks such a backing and reseeds from the view on the draft's first write, butvotes++reads first — it read base, wrote base + 1, and the override it emitted landed on the value already displayed. The click was invisible and the count stuck (or fell back) until truth caught up.Minimal sequence:
Fix
draftSeesOverrides(target): draft reads compose overrides whenever the pending backing is not the draft's own tentative clone (tentativePBs), applied to all four draft paths. Nothing changes for a draft that has opened its own backing (reads must see its writes) or for authoritative drafts.Tests
tests/optimistic-store-refetch-hold.test.tsgains "#2951 compose half": three votes in flight, the third made after the first landing, every visible value monotonic. Fails without the fix at click C (expected 2 to be 3). A ten-click replay on the app's exact schedule renders 1 through 10 with each click instant. Full signals suite, solid suite, and the web Loading/optimistic/store specs pass on the rebuilt dist.🤖 Generated with Claude Code