fix(signals): affects() as an action's first statement never lights isPending - #2888
Merged
ryansolid merged 2 commits intoJul 15, 2026
Merged
Conversation
🦋 Changeset detectedLatest commit: 06d821d The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 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
|
…() precedes the first optimistic write A companion (isPending/latest) written before its owner's first optimistic write — affects(store) as an action's first statement — created its lane with no parent link, so the owner's subsequent write merged the companion's subscribers into the store's async-carrying lane and their effects deferred to settle: tracked badges read false for the whole action, then blipped true for one tick. Adopt pre-existing companion lanes as children when the owner's lane is created, making the parent-child relation a property of the nodes rather than of write order. Fixes solidjs#2887 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
ryansolid
force-pushed
the
fix/affects-first-statement-lane
branch
from
July 15, 2026 05:23
41ad421 to
06d821d
Compare
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
Fixes #2887 —
affects(store)declared as the first statement of an action (before any optimistic write) never read as pending through trackedisPendingprobes for the duration of the action; the badge blippedtruefor a single tick at settle instead. Moving the sameaffectscall after the first write worked, exactly as the issue describes.Root cause
affects()on a lane-less node writestrueinto the node'sisPendingcompanion signal, which (being the first optimistic write) creates a lane for the companion. Companion lanes must stay children of their owner's lane so their effects flush immediately instead of waiting on the transaction's async — but the parent link was captured only at lane creation fromparentSource._optimisticLane. Withaffectsfirst, the owner has no lane yet, so the companion lane was born parentless. The action's optimistic write then reached the shared subscriber (the badge memo), found no parent-child relation inassignOrMergeLane, and merged the companion's lane into the store's async-carrying lane.runLaneEffectsskips lanes with pending async, so every tracked reader of the verdict deferred to settle.The mark itself was held correctly the whole time (refcount, untracked probes read
true) — only tracked/effect-driven readers were starved.Fix
When an owner's lane is created in
getOrCreateLane, adopt any lane its companions (_pendingSignal,_latestValueComputed) already created as a child of the new lane — the parent-child relation becomes a property of the nodes rather than of write order. Adoption is guarded to the companion's own unmerged, still-parentless root, so a lane group that has absorbed unrelated work is never re-parented.Testing
affects-propagation.test.tsreproducing the issue's setup (derived optimistic store + livemapArraysubscriber + tracked badge memo): fails without the fix (badge log stays empty during the action), passes with it.🤖 Generated with Claude Code