Conversation
…ross filtered keyed lists
…yed override (#3548) A18 supersession hands tracked readers the staged truth while the screen keeps the override until the owning transaction commits. A LATER action's write put a shared reader (a keyed list filtered on two rows' fields) back on a lane, and that lane pass — whose frame applies ahead of the commit — was served the truth too: one list re-derived from the landed placement while its neighbour still displayed the override, rendering the same card in two lanes. overrideRead: a lane pass reads what is on screen (the override) and is recorded in the owner's _gatedSubs for replay at the commit, under laneReadsCommitted's contract — the superseded drop notifies nobody. Pinned by tests/optimistic-move-duplicate-3548.test.ts (the report's five-step sequence over an async-iterable optimistic store, plus the post-settle reveal through the replay).
🦋 Changeset detectedLatest commit: 739404d 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 |
Merging this PR will not alter performance
Comparing Footnotes
|
Coverage Report for CI Build 35503195443Coverage remained the same at 72.722%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
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.
Fixes #3548.
Symptom
Two overlapping optimistic moves in a kanban-style UI — three keyed
<For>lists filtered from onecreateOptimisticStoreover an async stream — rendered the same card in two lanes while the store itself held it once. Reproduced exactly from the reported playground (ported to a signals-level test, red onnext):Cause
Not the provenance guard. The stream's landing runs mainline (
origin = 0), so A18 correctly treats it as a fresh answer and supersedes the override oncard0.lane; the lane memos re-derive as held transaction work and no frame publishes.The tear comes one step later. A fresh action's write to
card1.ordercreates a new lane that reaches lane 1'smapArraymemo — a reader shared betweencard0.laneandcard1.order. That memo now runs as a lane pass, andoverrideReadhad no lane arm: it fell through to the superseded branch and was served the staged truth (lane = 1). The lane pass published vialaneOverrideandrunEffect(LANE_RUN)applied it immediately. Lane 2's memo was not on the new lane, its plain recompute stayed held, and it kept displaying the override. Two readers, two paths, one torn frame.Fix
overrideRead(packages/signals/src/core/optimistic.ts): a lane pass reading a superseded override gets the displayed override — A18 (c), the applied screen keeps the override until the owning transaction commits — and the reader is recorded in the owner's_gatedSubsfor replay at that commit, under the same contract aslaneReadsCommitted/gatedRead. The replay is load-bearing:resolveOptimisticNodesdeliberately does not notify a superseded node's subscribers at the drop, so without it the lane-pass reader would never see the reveal. The test pins the post-settle reveal ([[1],[0],[]]) as well as the whole frame at every step.A17's "lane recomputes see the arrived truth" is read here as the demoted pass at supersession time, not a later lane's pass whose frame is applied pre-commit. The alternative — demoting such a pass off the lane entirely — also removes the tear but hides the newest action's optimistic write until the merged transaction commits; this fix keeps the newest write optimistic and the screen whole.
Verification
packages/signals/tests/optimistic-move-duplicate-3548.test.ts(red onnext, green here); a temporary web-level<For>spec reproduced the report's exact DOM onnextand passed with the fix.@solidjs/signals: 205 files, 3465 passed, 1 skipped.@solidjs/webjsdom suite: 96 files / 834 passed against rebuilt dists.activeTransition ??fallback was trimmed to stay under).RULES-INDEX.mdregenerated (A18 citation count).Changeset:
.changeset/fix-optimistic-move-duplicate-3548.md(@solidjs/signals: patch).Diagnosed and implemented by Claude via Cursor.