You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
2.0.0-rc until doesnt entangle the update resolving it with the action update #3164
This is an example demonstrating why the fix for #3123 (comment) is insufficient. Not entangling when until is set makes the granularity of the store decide what tears or not leading to weird issues where object might break type contracts without type errors.
changed the title [-]2.0.0-rc until doesnt entangle the two update resolving it with the action update[/-][+]2.0.0-rc until doesnt entangle the update resolving it with the action update[/+]on Sep 1, 2026
Fixed on next in a536e29 — and your instinct in the #3123 thread was right: we re-ruled this to entangle.
Truth landings under live optimism now fold: while any transaction retains optimism on the family (or an armed signal), fresh authoritative data — continuation yields, foreign refetches, replacing landings alike — stages into the retaining transaction and reveals atomically when it settles, the same semantics as a signal landing under an active override. Ordinary readers keep the committed view until that reveal, so no frame can compose override + landed truth (the union tear from your repro), and the authoritative version bump and the optimism's removal now land together.
until() and latest() tunnel through the hold: authoritative reads see the staged truth, and a landing staged under an active override wakes waiting until() predicates — that was the deadlock in your signal-path variant. This also retired the retained-setter replay machinery from the interim #3123 fix; the fold subsumes it.
Both repros from the report are pinned as regression tests (store union-tear + atomic reveal, signal until() wake). Will ship in rc.5.
@ryansolid this repro also kind of demonstrates why I'm a bit worried about entanglement semantics. They decide where the boundaries of consistency are. Choosing to make entanglement too fine grained in stores might cause issues like this where the intent of the user was to entangle with things they didn't actually touch. In this example we only touched status but intended to hold the entire object. Using equality to determine entanglement could have the same effect where the user writes something they assume should entangle but it happens to be equal and therefore doesn't. Cross primitives this is basically an unavoidable problem to some degree as we can't predict what signals you didn't set but within a store you might even break typescript types as demonstrated here if you let entanglement be based on sub store granularity.
I think the middle state is the same tearing problem. until() is the explicit statement that this update confirms the action, so the update making it truthy should entangle with it.
To clarify the AIs example this will seemingly work until either unentangled update happens to be waiting for something more than this exact thing as if this is all that is going on then the timing will just make them reveal/revert at the same time anyway.
You were right, and this is now in for rc.5: until() entangles the update that flips it (1ece086f, refined in 1ac14eb6 on next).
The semantics landed exactly on your framing — until() is the user's explicit declaration of what confirms the action, so we don't guess at relatedness:
When an awaited predicate settles truthy, whatever live transition (or ambient batch) staged the values it read IS the confirming event. Its staged cargo moves into the awaiting action's transaction and reveals at the joint settle. Your middle state — saving=true, version=new, data=new — can no longer paint: ordinary readers hold the pre-confirmation view until the action's optimism reverts and the confirmation commits, in one frame.
Falsy evaluations entangle nothing. Unrelated traffic on the watched sources (a non-flipping landing, another action's writes) reveals on its own schedule. The boundary of consistency is the flip itself, not the store/signal graph — which also answers your granularity worry for the cross-primitive case: the predicate names the condition, so entanglement is exactly as coarse or fine as what you wrote.
Within a store family, the fold from the previous fix already holds at family granularity (touching status holds the whole session object — your union repro), so sub-store granularity can't split a typed object into a shape no timeline contained.
The steal takes only the confirming transition's staged values, not its future: an open stream that echoes your write can flip the predicate without chaining the action to landings that haven't happened (a full merge deadlocked exactly that way — the iterable's reporter never completes). latest() and other authoritative reads still tunnel through the hold, and until() keeps its timeout/AbortSignal valves so a dropped confirmation can't hold a transaction forever.
Your exact scenario (signal-side optimism + foreign store stream confirming) is pinned as a regression test, along with the non-flipping-traffic case, a foreign signal-transition case, and an already-truthy fast path. Thanks for pushing on this one — the report was correct that fold-only semantics made the store's granularity decide what tears.
I discussed it a bit more with AI and seems like the store family thing mostly solves the granularity but only for optimistic stores. Similar issues like this could happen with regular stores as well so might be reasonable to consider something similar for regular stores. But I understand there is a tension here given some user will use a mega store for their entire app. In the future maybe we could explore a api that allow you to express these boundaries when constructing the store somehow but given the current api there probably isn't a perfect solution.
Activity
[-]2.0.0-rc until doesnt entangle the two update resolving it with the action update[/-][+]2.0.0-rc until doesnt entangle the update resolving it with the action update[/+]fix: truth landings fold under retained optimism — atomic reveal at s…
fix: reconcile the patch channel with the #3164 fold ruling
ryansolid commentedon Sep 1, 2026
Fixed on
nextin a536e29 — and your instinct in the #3123 thread was right: we re-ruled this to entangle.Truth landings under live optimism now fold: while any transaction retains optimism on the family (or an armed signal), fresh authoritative data — continuation yields, foreign refetches, replacing landings alike — stages into the retaining transaction and reveals atomically when it settles, the same semantics as a signal landing under an active override. Ordinary readers keep the committed view until that reveal, so no frame can compose override + landed truth (the union tear from your repro), and the authoritative version bump and the optimism's removal now land together.
until()andlatest()tunnel through the hold: authoritative reads see the staged truth, and a landing staged under an active override wakes waitinguntil()predicates — that was the deadlock in your signal-path variant. This also retired the retained-setter replay machinery from the interim #3123 fix; the fold subsumes it.Both repros from the report are pinned as regression tests (store union-tear + atomic reveal, signal until() wake). Will ship in rc.5.
GabbeV commentedon Sep 1, 2026
@ryansolid this repro also kind of demonstrates why I'm a bit worried about entanglement semantics. They decide where the boundaries of consistency are. Choosing to make entanglement too fine grained in stores might cause issues like this where the intent of the user was to entangle with things they didn't actually touch. In this example we only touched status but intended to hold the entire object. Using equality to determine entanglement could have the same effect where the user writes something they assume should entangle but it happens to be equal and therefore doesn't. Cross primitives this is basically an unavoidable problem to some degree as we can't predict what signals you didn't set but within a store you might even break typescript types as demonstrated here if you let entanglement be based on sub store granularity.
GabbeV commentedon Sep 1, 2026
Having AI explain the change that landed here to me also revealed that we still don't entangle based on until.
I had AI write up the reasoning:
I still think
until()itself needs to entangle with the update that makes it truthy.The new fold rule only catches this when the optimism and confirmation touch the same store family. For example:
If the stream lands
version+ new data, that can still reveal beforesavingreverts because the optimism lives on a different primitive.That gives:
I think the middle state is the same tearing problem.
until()is the explicit statement that this update confirms the action, so the update making it truthy should entangle with it.GabbeV commentedon Sep 1, 2026
To clarify the AIs example this will seemingly work until either unentangled update happens to be waiting for something more than this exact thing as if this is all that is going on then the timing will just make them reveal/revert at the same time anyway.
fix: reconcile the patch channel with the #3164 fold ruling
feat(signals): until() flip-entanglement — confirming transitions rev…
ryansolid commentedon Sep 1, 2026
You were right, and this is now in for rc.5:
until()entangles the update that flips it (1ece086f, refined in1ac14eb6onnext).The semantics landed exactly on your framing —
until()is the user's explicit declaration of what confirms the action, so we don't guess at relatedness:saving=true, version=new, data=new— can no longer paint: ordinary readers hold the pre-confirmation view until the action's optimism reverts and the confirmation commits, in one frame.statusholds the whole session object — your union repro), so sub-store granularity can't split a typed object into a shape no timeline contained.latest()and other authoritative reads still tunnel through the hold, anduntil()keeps itstimeout/AbortSignalvalves so a dropped confirmation can't hold a transaction forever.Your exact scenario (signal-side optimism + foreign store stream confirming) is pinned as a regression test, along with the non-flipping-traffic case, a foreign signal-transition case, and an already-truthy fast path. Thanks for pushing on this one — the report was correct that fold-only semantics made the store's granularity decide what tears.
fix: reconcile the patch channel with the #3164 fold ruling
test(signals): pin until() entanglement's extended-hold and joint-set…
GabbeV commentedon Sep 1, 2026
I discussed it a bit more with AI and seems like the store family thing mostly solves the granularity but only for optimistic stores. Similar issues like this could happen with regular stores as well so might be reasonable to consider something similar for regular stores. But I understand there is a tension here given some user will use a mega store for their entire app. In the future maybe we could explore a api that allow you to express these boundaries when constructing the store somehow but given the current api there probably isn't a perfect solution.
fix(signals): draft reads compose overrides over a truth-staged backi…
5 remaining items
fix(signals): store twins of the lane-authority fixes; late readers o…
fix(signals): own-source arrival supersedes the override, with action…
fix(signals): store twins of the lane-authority fixes; late readers o…
fix(signals): own-source arrival supersedes the override, with action…
fix(signals): store twins of the lane-authority fixes; late readers o…
fix(signals): own-source arrival supersedes the override, with action…
fix(signals): store twins of the lane-authority fixes; late readers o…
fix(signals): own-source arrival supersedes the override, with action…
fix(signals): store twins of the lane-authority fixes; late readers o…
fix(signals): optimistic settle verdicts — combined isPending on the …
fix(signals): held truth is masked from lane passes only; deriving re…
fix(signals): a fold repeats no unchanged presence to a held node (#3743
fix(signals): a fold repeats no unchanged presence to a held node (#3743
fix(signals): a keyed arrangement guess folds a correction instead of…
fix: optimistic release without torn frames (sink slot records, keyed…