Repository navigation
fix(signals): a staged read of a node born staged is not re-queued at the lane seam - #3882
Conversation
… the lane seam Since #3869, verdict-lane work that read a staging of the flush is re-derived on the committed world while the frame stays parked (`stagedReaders`, the lane seam). A node born into the parked frame has no committed value: the re-run reads the same staging, registers again, and the seam re-queues it every round until flush throws "Potential Infinite Loop Detected" (semantic fuzzer, mount-under-hold: a `latest()`-gated Show mounting a fresh Loading under an action hold, 29 cases across seeds 3289 and 91501). `stagedRead` keeps REACTIVE_STAGED_READ for such a read but no longer lists the pass for re-derivation. The frames return to pre-#3869's (the content shows the held value while held; A29's fallback frame is still open, pinned as test.fails). Co-authored-by: Claude via Cursor <noreply@cursor.com> Co-authored-by: Cursor <cursoragent@cursor.com>
🦋 Changeset detectedLatest commit: 641078d 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)
|
Coverage Report for CI Build 37691137925Coverage remained the same at 76.43%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Fixes the RC fuzzer gate's 29
Potential Infinite Loop Detectederrors (mount-under-hold, seeds 3289 / 91501), introduced by #3869.Shape
An action holds
x.<Show when={latest(x) > 0}>(gated through a memo) opens and mounts a new<Loading>whose content readsxdirectly, with no load of its own. The firstflushafter the action's write never settles.Root cause
packages/signals/src/core/core.ts,stagedRead(called fromserve), working with the lane seam inpackages/signals/src/core/lanes.ts(GlobalQueue._laneSeams, the verdict-lane re-run that #3869 added:!blocked(l._parent)).STATUS_UNINITIALIZED, and has no committed value.Show's children is verdict-lane work. It reads that node's staging. Since fix(signals): seat a first pass in its creator's lane, never a verdict lane (#3835, #3851) #3869,stagedReadlists every lane pass instagedReaders, verdict lanes included.Fix
servepasses the node it read tostagedRead. A read of an uninitialized (born staged) node still setsREACTIVE_STAGED_READ, which the born-held clause androutedepend on, but it is no longer listed for re-derivation. One condition at the existing decision site.Frames
The flush settles and the commit reveals
content 1. While held, the content showscontent 1beside the committedx = 0. That is exactly the pre-#3869 frame (the MH1 failure these cases already had atfa371c3e7). A29's "freshLoadingshows its fallback while held" frame is still open: fixing it would mean the boundary's output not being born held, which is a larger change. It is pinned astest.failsso it flips when fixed.Tests
packages/signals/tests/verdict-mount-loading-flush-loop.test.tscovers case 119 and case 323 (the same shape plus a reader ofxalready on screen). The no-throw and commit-frame tests throw onnextand pass here; the A29 fallback frame istest.failsfor both.Fuzz evidence (rev 23,
030e77f2b, source-only copy, seeds 3289 / 91501)bc51ada93latestPer-case diff across every cohort, both seeds: exactly 29 cases changed, all from
error(infinite loop) tofail MH1, the status they had before #3869. Nothing else moved.Size
+15 B minified wherever signals is bundled (under the 20 B allowance;
gate.mjspasses). Brotli moves by layout noise.Public API changes
None.