Repository navigation
fix(signals): a pass joined only through born-held nodes is not re-derived by a lane entry (#3969) - #3980
fix(signals): a pass joined only through born-held nodes is not re-derived by a lane entry (#3969)#3980ryansolid wants to merge 1 commit into
Conversation
…rived by a lane entry (#3969) A render effect read a memo born held into a parked change and then a verdict lane's node (isPending). enterLane treated the pass as lane work that read a held write (REACTIVE_JOINED) and queued it to re-derive on the committed world, but a born-held node has none: the re-run read the same staging. The lane held, the effect re-ran on the screen, the lane showed and re-ran it, every round, until the loop guard. Under the dev server every component runs in a transparent memo (solid-refresh), which is the born-held node. A read of a node born staged no longer marks REACTIVE_STAGED_READ (stagedRead already kept it out of stagedReaders), and enterLane's repair needs a staged read beside the join. Fixes #3969 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVm3wseHt9wRTY8LfPiHmQ
🦋 Changeset detectedLatest commit: 6262b38 The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 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 |
Size (brotli, eager entry chunk)
|
Merging this PR will regress 1 benchmark
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | createStore setter: delete + set one root key (#3044 overlay) |
1.8 ms | 2.1 ms | -14.28% |
| ⚡ | memo + sync render effect only (reference) |
32.2 ms | 27.9 ms | +15.48% |
| ⚡ | projection derive: write one NESTED field (reference) |
2.4 ms | 2.1 ms | +14.28% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing fix/born-held-lane-loop-3969 (6262b38) with next (e9d549f)1
Fixes #3969
Why only in the browser: the issue reproduces under the Vite dev server, not in a production build or under jsdom, because the ingredient is hot reload. solid-refresh runs every component inside a transparent memo (
createProxyinsolid-js/refresh). Built the same app without HMR in headless Chromium: no loop; undervite dev: two guard trips per Save, as reported.hot: falsemakes them go away. The new jsdom spec wraps components the way solid-refresh does and reproduces it deterministically (Spinner wrapped, App wrapped, or both; unwrapped passes onnexttoo).Mechanism: Save's
setSelected(false)swaps the<Show>to its fallback, held with the refetch. The fallback mountsSpinnerinside its HMR memo, which is born held into the parked change. The insert effect for the<Show>reads that memo (joining the hold through it,REACTIVE_JOINED) and thenisPending(project)'s verdict lane.enterLanetreated the pass as lane work that read a held write and queued it to re-derive on the committed world, but a born-held node has no committed value: the re-run read the same staging. The lane held; the effect re-ran on the screen and registered for the lane's reveal; the lane showed and re-ran it; repeat until the guard.Fix:
stagedReadalready kept reads of a node born staged out ofstagedReaders("a re-run would read the same staging and re-queue every round"); it now doesn't mark them as staged reads at all, andenterLane's repair needs a staged read beside the join. A pass whose held reads were all of born-held nodes is left alone; one that read a held write with a committed value is re-derived as before.No public API change. Size: core floor −1 B minified,
isPending/latest+10 B minified / −1 B brotli (local). Tests:packages/web/test/hmr-spinner-loop-3969.spec.tsx(four wrappings; also pins that the screen keepsselectedwhile the swap is held). Signals suite: 5155 passed, 22 expected fail. Web suite: unchanged (the 30 failures inelement-claims/select-async-valuealso fail onnextlocally — stale native compiler build here).I couldn't reduce it to a signals-only test with
createMemo/createRenderEffectstand-ins forShowandinsert; the web spec is the pin.🤖 Generated with Claude Code
https://claude.ai/code/session_01LVm3wseHt9wRTY8LfPiHmQ
Generated by Claude Code