Repository navigation
fix(signals): a derived store's propagated error reaches its readers once - #4463
Conversation
…once propagateStatus handed the derive's family a status without its error, so errorFamily's same-error skip could not apply and a sync derive over a sync memo re-ran its reader's error arm twice in one flush (#4457 review). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
…-x1ore2-family-status-error
🦋 Changeset detectedLatest commit: 10663ec 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)
Bundled with Rolldown (what Vite ships), brotli q11, decimal KB. A scenario fails only when it is over its brotli cap and its minified size is more than 20 B over the minified recorded with the cap; over the cap within that allowance is brotli layout noise and passes with a warning. Caps and their recorded minified in |
Coverage Report for CI Build 41073676164Coverage remained the same at 78.1%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Merging this PR will improve performance by 10.46%1 warning
Performance Changes
Tip Curious why performance improved? Comment Comparing Footnotes
|
|
I don't think CodSpeed's −13.5% on The change here only adds an Generated by Claude Code |
…-x1ore2-family-status-error
…rror Raise the base + router and compiled hydrating caps (brotli layout from +2 B minified) and give StoreFamily.st its error parameter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
…y a re-run The derive pending by propagation (familyStatus -> wakeFamily) was marked blocked so its source's settle re-ran it. For an async derive that re-run is a new flight: when the source's rejection was staged, the walk in stageError re-ran the derive in the same flush, ahead of the commit that publishes the error to it. The new flight woke the family again, so a reader beside isPending showed a second pending frame before the error (#4463's error-once test, D-41). The settle walk now settles the family instead (familyStatus with no status -> settleFamily) when it retires a derive's pending without a pass to re-run, as a memo's readers settle through its dependents (F-9, A9). The error still reaches the derive at the commit (F-12, D-41), and the re-ask that nets to the committed question ends isPending (F-9). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
…y a re-run (#4493) * fix(signals): a derived store's derive re-runs when a source it pends on settles `createStore(fn)` over an async memo. The derive went pending on the memo's flight by propagation (`familyStatus` -> `wakeFamily`), and the store's readers were parked on the derive itself. When the flight then settled without the derive re-running (a rejection staged in its transaction, or a re-ask that returned to the committed question), the settle walk retired the memo from the derive but never reached the readers parked on it: `isPending` stayed true forever (F-9), and the staged rejection's transaction never landed, so the error never showed (F-12, D-41). The wake now marks the derive blocked, as a pass that threw is, so the settle re-runs it and its commit settles the family. Found by the async-gen rerun on rc.20 (pair E3, rows #1 and #9). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2 * chore(size): record the derived-store settle fix's sizes over next @ 7135a77 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2 * fix(signals): a derived store's readers settle with its derive, not by a re-run The derive pending by propagation (familyStatus -> wakeFamily) was marked blocked so its source's settle re-ran it. For an async derive that re-run is a new flight: when the source's rejection was staged, the walk in stageError re-ran the derive in the same flush, ahead of the commit that publishes the error to it. The new flight woke the family again, so a reader beside isPending showed a second pending frame before the error (#4463's error-once test, D-41). The settle walk now settles the family instead (familyStatus with no status -> settleFamily) when it retires a derive's pending without a pass to re-run, as a memo's readers settle through its dependents (F-9, A9). The error still reaches the derive at the commit (F-12, D-41), and the re-ask that nets to the committed question ends isPending (F-9). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2 * chore(size): record the derived-store settle fix's sizes over next @ d0fc285 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2 * fix(signals): settle a derive's family only in a store build On next @ 643a7a7 the ungated `else (node as any).fam?.st()` laid out +20 B brotli on hello world (9,993 B, over its 9.98 KB cap) from -3 B minified. A derive's family exists only when the store module is loaded, which is what sets `storeParkHook`, so the arm is gated on it and folds away in store-free bundles: every one is 0 B vs next, hello world included (9,973 B). The settle walk's `_blocked` clear is back to next's form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2 --------- Co-authored-by: Claude <noreply@anthropic.com>
Before: a sync derived store (
createStore(fn)orcreateOptimisticStore(fn)) over a sync memo handed each new error to its reader twice in one flush, so an effect'serrorarm ran twice per error. The derive learns the error twice: once by propagation (corepropagateStatus→ the family'sfamilyStatus) and once from its own pass re-run against it. #4457 added a same-error skip toerrorFamily, but the propagation call passed only the status, so the skip never applied there. Next behaves the same for both store kinds, so this predates #4457.After: each error reaches the reader once. A memo throwing E1, then E2, then recovering logs
ERR E1,ERR E2,v 0, not each error twice.How:
propagateStatuspasses the error along (fam?.st(status, error)), andfamilyStatushands it toerrorFamily, which skips a reader already errored with that same error. The stale comment that said there was no error to compare is updated.Size vs next @ cddaa76, measured locally (linux): +2 B minified on every floor and +6 B where stores ship. Hello world is 9,974 B brotli, under 10,000. Brotli moves −88 to +53 B from prop mangling. No cap changes.
Tests: a new case in
packages/signals/tests/derived-store-error-delivered-once.test.tsfor both store kinds. It fails on next, with each error logged twice, and passes with the fix. In the signals suite, 5,982 tests passed. The only failures were the 35dist-artifactstests, which need a dist build that worktree didn't have.Follow-up to the #4457 review.
🤖 Generated with Claude Code
https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2