Repository navigation
fix(signals): a reader mounted mid-flight reads a derived store like its memo (#4491) - #4500
Conversation
…its memo (#4491) `createStore(d => { d.v = m1() })` over an async memo whose input reads `latest()` has a derive that is a verdict reader through what it reads (D-25 / F-6). At a re-ask it is served `m1`'s committed value and settles over it, so its leaves read as final: a reader that mounted mid-flight showed the old question's answer before the new one (in the mounting flush once #4479 skips the unread derive, at the landing before it), where a reader of `m1()` is held (F-2). D-30: a derived store reads like its memo. `pullFamily`: a tracked reader of a settled verdict-reader derive with no window of its own reads the flights among the derive's deps, as the memo's reader would, so it is held; the derive is that hold's work until it answers. `runProjectionComputed`: a derive whose pending is on its leaves' readers (`fam.woke`) reads plainly, as a pending node's pass does (#3884), so its landing is the answer the held reader shows. Store-only: no frozen floor moves. The #4479 review's probe forms (key read, Object.keys, JSON) are pinned against the memo form: mounted mid-flight with and without a plain write beside the flight, on screen at the re-ask, and mounted after the landing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
…is never written outside its pass Review of a78d91a: a verdict reader exists only with the verdict engine, so both halves of the fix now sit behind `observeFlightHook`. Store bundles without isPending/latest fold them away (createStore, compiled CSR and compiled hydrating at +0 B vs next), so the cap raises recorded for createStore and compiled CSR are dropped. The pull no longer clears the derive's CONFIG_VERDICT. That clear was sticky: when the flight's landing equalled the screen, the derive did not re-run and stayed a non-verdict reader into its next flight. The read now marks the family (`fam.w`), the derive's next pass consumes the mark and reads plainly, and a read that finds no flight up clears it. The posture stays the passes' own (core `recompute`). New tests: after the held mount, a later flight runs as for a store read from the start and as the memo form, after a changed landing and after an equal one. The equal-landing case fails on a78d91a (verdict=false after the landing). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
🦋 Changeset detectedLatest commit: 6d71df1 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 41118328857Coverage remained the same at 78.1%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Merging this PR will not alter performance1 warning
Comparing Footnotes
|
|
Size gate ( Generated by Claude Code |
|
CodSpeed's two flags on this head are both reference benches that move without code changes on their path. Generated by Claude Code |
…4499, #4500, #4493, #4488, #4498) (#4507) next @ b5077ea is over six caps after the batch merged back to back (each fix PR carried no ledger edits, per the batch process). Re-records, measured over next @ b5077ea (linux): - signals: + createStore: 14.97 -> 14.99 KB, minified 43,924 -> 43,956 - signals: + isPending/latest: 11.29 -> 11.35 KB, minified 31,101 -> 31,245 - app: compiled CSR: 26.33 -> 26.36 KB, minified 80,035 -> 80,074 - page: live server components (frozen floor): 38.98 -> 39.10 KB, minified 117,858 -> 118,033 - page: compiled live server components: 42.95 -> 43.01 KB, minified 126,018 -> 126,181 - page: live + router: 48.58 -> 48.65 KB, minified 143,269 -> 143,439 and the recorded minified only (caps unchanged) where the batch drifted more than 20 B: hydrating + every store (98,535 -> 98,561), compiled hydrating (100,803 -> 100,836), base server components (frozen, 101,675 -> 101,706), compiled base server components (108,048 -> 108,081), base + router (125,503 -> 125,539). Hello world is 9,971 B (cap 9.98 KB). The build leaves the pinned mangle-props.json unchanged. Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2 Co-authored-by: Claude <noreply@anthropic.com>
Before: take
createStore(d => { d.v = m1() })over an async memo whose input readslatest(). A reader that mounted while m1 was re-asked showed the old question's answer. On the same mount, a reader ofm1()is held (F-2).After: the store reader is held like the memo's reader, and shows the new answer at the landing (D-30: a derived store reads like its memo). This holds for key reads,
Object.keysandJSON.stringify, with or without a plain write beside the flight.Cause: the derive is a verdict reader through what it reads (D-25 / F-6). At a re-ask it is served m1's committed value and settles over it, so its leaves read as final.
How:
pullFamily: a tracked reader of a settled verdict-reader derive, with no window of its own, reads the flights among the derive's deps, as the memo's reader would. So it is held, and the derive is that hold's work until it answers.runProjectionComputed: a pass withfam.wokeorfam.wset clearsCONFIG_VERDICTfor that pass and reads plainly, as a pending node's pass does (isPending/latest on a superseded transition depend on whether the parked async memo also reads them #3884). Its landing is then the answer the held reader shows. Note this is wider than the [2.0 / next] A reader that mounts while an unread derived store's flight is up shows the old question's answer (D-30, F-2) #4491 shape: it applies to any pass where the derive's pending sits on its leaves' readers (fam.woke), not only after the newpullFamilyread. The signals and web suites and the async-app generator are unchanged by it.observeFlightHook, because a verdict reader exists only with the verdict engine. Store bundles withoutisPending/latestpay 0 B.Rules: D-30, F-2, F-6, D-25.
Not covered here:
pullFamilyonly looks at the derive's direct deps, so a memo between the derive and the flight (d.v = m2()withm2 = memo(() => m1() * 10)) still shows the old answer. That shape is also wrong for a plain memo reader (createMemo(() => m1())mounted mid-flight showsr:2, r:4instead ofr:4), so it's a core bug in the memo form, filed and fixed separately.Tests:
tests/unread-derive-midflight-mount-4491.test.ts(11 cases, pinned against the memo form). Signals 6091 passed / 17 expected fail; web 1579 passed / 7 expected fail.Size (client, vs next 643a7a7; no ledger edits in this PR):
Every store bundle and every other scenario is unchanged; hello world stays at 9,973 B.
Closes #4491
Generated by Claude Code