Skip to content

fix(signals): keep isPending until a changed lane answer reveals (#4685) - #4692

Open
santhiprakash wants to merge 2 commits into
solidjs:nextfrom
santhiprakash:fix-4685-pending-until-reveal
Open

santhiprakash wants to merge 2 commits into
solidjs:nextfrom
santhiprakash:fix-4685-pending-until-reveal

Conversation

@santhiprakash

@santhiprakash santhiprakash commented Nov 5, 2026 •

Copy link
Copy Markdown

Summary

Fixes #4685.

isPending(m) cleared as soon as a lane derivation's flight landed, even when the lane was still blocked by a sibling's pending work — so the new answer sat in laneValueOf while the screen still showed the old value, and isPending reported no pending change over a stale display.

The verdict probe now also compares the lane's latest answer (laneValueOf) against what the reader sees (v — the display, or the latest view inside latest()). A differing, unrevealed answer keeps the probe pending; an equal one still clears early (A24). The tracked probe parks on the lane's _reruns so it re-derives at the lane's seam — the flip happens at the reveal, which can precede and outlive the parent's landing.

In reruns, a verdict-only reader (CONFIG_VERDICT without REACTIVE_FRAME_READ) no longer marks the rerun frame-producing — it only re-derives a verdict beside the screen, so the landing's releases need not wait for it. Without that, parking the probe would delay the parent by a round and eat the first reveal frame (latest-first-one-pass-4402).

Three createOptimistic assertions updated — they pinned the reported buggy window rather than the ruled semantics:

  • Each asserted isPending(m) is false in the window where m's changed answer has landed into a lane still blocked by a sibling flight — the exact shape 2.0.0-rc.21: isPending clears before a changed optimistic result is revealed #4685 reports as a bug.
  • isPending holds until merged lane completes, not just individual async describes the desired behavior in its own comment ("isPending for each path should stay true until the ENTIRE merged lane resolves") while asserting the opposite.
  • The documented mechanisms agree with the issue: A10 ("a landed answer awaiting reveal … reads committed with pending beside it") and D-23 cause (iii) ("a fresh value landed but held uncommitted by a hold it is entangled with").

If per-flight clearing is the intended contract for merged lanes after all, I'm happy to rework — these assertions can be restored and the issue triaged accordingly.

How did you test this change?

New regression test ispending-unrevealed-lane-landing-4685.test.ts models the issue's repro: an optimistic guess under a never-resolving action, left landing before the shared right flight:

"left 0 | pending false | total 0"   baseline
"left 0 | pending true | total 0"    flights up
"left 0 | pending true | total 0"    left landed, unrevealed (previously false)
"left 10 | pending false | total 30" reveal
  • pnpm build clean; pnpm vitest run in packages/signals: 480 files / 6694 tests pass (14 expected-fail, 2 skipped).
  • Size: the rerun guard costs +29 B minified where reruns is retained without the verdict path (the floors, + createStore, observe + attribution, base + router); the probe's lane-answer comparison brings it to +128–129 B minified where the verdict path is (+ isPending/latest, the live pages). Caps for the rerun-guard-only scenarios are raised measured by CI (Size run 42031370774); after the rebase over fix(signals): a lane reveals whole beside its parent's landing (#4677) #4687 three scenarios are re-recorded over the new base measured by CI (run 42092720174: + isPending/latest 12.29 KB / 33,743 B minified; the live-page floor 40.35 KB / 121,282 B — see the Size-Exception below; run 42133064937: + every store primitive family 33.44 KB / 100,576 B minified). scenarios.js records the smaller shapes rejected for changing semantics — a stale-reader park delays the landing's releases, and a verdictRead park re-routes the reader's pass into the lane.
  • docs/RULES-INDEX.md regenerated (verdict.ts A24 citation count); node scripts/rules-index.mjs --check passes.

Size-Exception: page: live server components is a frozen floor; the +129 B minified there is the fix's verdict-probe cost described above, not slack — the smaller shapes change semantics.

…idjs#4685)

A lane derivation's flight could land while its lane stayed blocked by a
sibling's pending work: laneValueOf already held the new answer but the
reader's screen still showed the old value. verdictValue only probed
flying, so isPending cleared in that window — reporting no pending change
while the displayed value was still stale.

Now the verdict probe also checks whether the lane's latest answer differs
from what the reader sees (the display, or the latest view inside
latest()); a differing unrevealed answer keeps the probe pending, and the
tracked reader parks on the lane's reruns so it re-derives at the lane's
seam rather than only at the parent's landing. In reruns, a verdict-only
reader (no REACTIVE_FRAME_READ) no longer marks the rerun frame-producing,
so its re-derivation doesn't delay the landing's releases.
@changeset-bot

changeset-bot Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 788a0af

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 15 packages
Name Type
@solidjs/signals Patch
test-integration Patch
@solidjs/web Patch
@solidjs/babel-plugin Patch
@solidjs/compiler Patch
@solidjs/diagnostics Patch
@solidjs/element Patch
@solidjs/h Patch
@solidjs/html Patch
solid-js Patch
@solidjs/universal Patch
board-example Patch
canvas-example Patch
server-dbmon-example Patch
todos-server-example Patch

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

@codspeed

codspeed Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

1 warning, 1 note

⚠️ 1 benchmark is dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

ℹ️ This run is excluded from the branch history

This commit was force-pushed, so this run no longer appears in the branch's performance history.

✅ 201 untouched benchmarks
⏩ 5 skipped benchmarks1


Comparing santhiprakash:fix-4685-pending-until-reveal (b685aed) with next (4fe7532)

Open in CodSpeed

Footnotes

  1. 5 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

Keeping isPending true over an unrevealed changed lane answer costs
+29 B minified where the rerun guard alone is retained and +121-129 B
where the verdict path is. Raise the six moved scenario caps and their
recorded minified at the CI measurements (Size run 42031370774), with
the mechanism and the rejected smaller shapes in scenarios.js; the
frozen page floor goes under the Size-Exception line in the PR body.
@santhiprakash
santhiprakash force-pushed the fix-4685-pending-until-reveal branch 3 times, most recently from b685aed to 788a0af Compare November 6, 2026 00:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant