Repository navigation
fix(signals): a derived store's write in a lane pass is the lane's derivation - #4478
Conversation
…rivation
`createStore(d => { d.v = m() })` with `m = createMemo(() => 1 + latest(s))`.
An action writes `s` and stays open: `m` is a verdict reader, so its pass
runs in the action's verdict lane and shows the new value now (F-6, D-25).
The derive reads `m`, so its pass is that lane's work too, but its leaf
writes are plain signal writes: they were staged in the flush, which joined
the action, and the store showed the old value until the action ended. A
memo derived the same way publishes into the lane. The derive's write in a
lane pass now becomes the lane's derivation on the leaf (its value the
lane's, the committed value untouched beneath; the lane's end commits it).
Found by the async-gen rerun on rc.20 (pair E3, rows #2 and #7).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
…t @ 7135a77 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
# Conflicts: # scripts/size/scenarios.js
…t @ e49b0c7 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
Re-measured after merging next @ ab3d2ad: the same bytes; the ledger lines name the new base. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
🦋 Changeset detectedLatest commit: 5c002e8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 15 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)
|
Coverage Report for CI Build 41109664498Coverage remained the same at 78.1%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Merging this PR will degrade performance by 2.74%2 warnings
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
|
CodSpeed flags
This PR changes only I'll leave the check as it is rather than re-run it. If it reproduces on the next push that carries code, I'll profile the bench against next on the same runner. Generated by Claude Code |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
…gine Store bundles without lanes pay 0 B: createStore and compiled CSR and hydrating drop their cap raises. The lane bundles re-record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
…tch PR) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
…4475, #4478, #4459) (#4501) Next @ 643a7a7 sits over four caps after the batch merged back to back (each fix PR carried no ledger edits, per the batch process). Re-records, measured over next @ 643a7a7 (linux): - signals: + isPending/latest: 11.19 -> 11.29 KB, minified 30,840 -> 31,101 - page: live server components (frozen floor): 38.91 -> 38.98 KB, minified 117,566 -> 117,858 - page: compiled live server components: 42.87 -> 42.95 KB, minified 125,724 -> 126,018 - page: live + router: 48.51 -> 48.58 KB, minified 142,987 -> 143,269 and the recorded minified only (caps unchanged) where the batch drifted more than 20 B: hydrating + every store (98,463 -> 98,535), base server components (frozen, 101,638 -> 101,675), compiled base server components (108,007 -> 108,048), base + router (125,470 -> 125,503). Hello world is unchanged at 9,973 B (cap 9.98 KB). Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2 Co-authored-by: Claude <noreply@anthropic.com>
… derives from (#4451) (#4522) A derive's lane-pass write to a key nobody had read yet stages on the container (`stageOn` → `holdWithDerive` lists it in the lane, #4478); the leaf born from that staging in the same flush was a plain staging the park then held with the frame, so frame readers saw the committed `undefined` beneath while the memo the derive reads showed the lane's value. An `Errored` over the store stayed on its fallback until the action landed; a memo over the key derived NaN. `bornStaged` now hands a leaf born from a lane container's staging (OVERRIDE without GUESS) to `holdWithDerive` with the container's lane: the staging becomes the lane's derivation, as the write would have left a leaf that existed then (E3, D-25, D-39). `holdWithDerive`'s `userWrite` discriminator moves to its callers so the born leaf — a read, possibly inside a user setter's body — is classified by the container's staging, not the caller's. The #4137 pin's mid-action screen changes from `["a",null]` to `["a","b"]` (accepted by the maintainer): the derive's new key is the lane's derivation for a leaf born after the write as for one that existed; the memo twin shows `["a","b"]` on both. Size: "app: hydrating + every store primitive family" 32.48 -> 32.50 KB (+48 B minified / +58 B brotli over next on that scenario; core floor, hello world and every bundle without stores 0 B). Accepted by the maintainer. Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Claude <noreply@anthropic.com>
Before: take
createStore(d => { d.v = m() })withm = createMemo(() => 1 + latest(s)), and an action that writessand stays open.mis a verdict reader, so its pass runs in the action's verdict lane and shows the new value at once (F-6, D-25). The derive readsm, so its pass is that lane's work too. Its leaf writes, though, were plain stagings that parked with the action, so the store showed the old value until the action ended while the memo showed the new one.After: the store shows what the memo shows. A derive's write in a lane pass is the lane's derivation, the same way a memo derived from
mpublishes into the lane.How: in
holdWithDerive(store/store.ts), a leaf write in a lane pass (passLane, not a user write) becomes the lane's derivation on the leaf. It lists the node on the lane, sets_x._laneandCONFIG_OVERRIDE, and leaves the committed value untouched underneath; the lane's end commits it. The branch is gated on the lane engine (laneStageHook), so store bundles without lanes pay 0 B.Size vs next @ d0fc285 (after #4479), measured locally (linux). Only bundles with lanes pay; the CI size report has the exact brotli deltas.
page: base server components(frozen floor)page: live server components(frozen floor)page: compiled base server componentspage: compiled live server componentsapp: hydrating + every store primitive,page: base + router,page: live + routerSize-Exception: the base and live server-components floors carry the derived store's lane-pass write (D-25, F-6), gated on the lane engine. Base server components keeps its 33.77 KB cap and re-records its minified 101,638 → 101,675 (+28 B of this PR, the rest next's own drift). Live server components goes 38.91 → 38.93 KB and re-records 117,566 → 117,583. Hello world and the core, simple-app and hydrating floors are unchanged. Correctness.
How did you test this change?
New
packages/signals/tests/derived-store-lane-derive.test.tsandpackages/web/test/async-gen/store-derive-lane-e3.spec.ts.Suites after merging next @ d0fc285:
Found by the async-gen rerun on rc.20 (pair E3, rows #2 and #7).
🤖 Generated with Claude Code
https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2