Skip to content

[2.0 / next] laneRead's D-28 verdict-seat reset compares verdict lanes by identity; a merged transaction's lane reads as another hold's #4427

Closed

Description

@ryansolid

Summary

laneRead's D-28 reset (#4275, #4284) — "a verdict lane's seat does not carry a leaf into another lane" — compares verdict lanes by object identity: passLane !== l && passLane._parent._verdict === passLane. After a transaction merge (A._into = B), A's verdict lane and B's verdict lane are two objects for one hold. A render effect seated as B's verdict reader (its latest() of a node B holds routed it to verdictLane(txOf(el)), resolved) then reads a derivation that still sits in A._verdict (its _x._transaction was stamped before the merge): the reset fires as if the leaf had crossed into another hold's lane, passLane is cleared, and the leaf is served the staging through the L7' arm (c held by the flush's transaction) instead of staying seated and reading the lane's value.

The hold is the same; only the lane object differs. The leaf leaves its own hold's verdict seat for the rest of the pass.

Reproduction

async-gen seed 3289 case #43 (packages/web/test/async-gen):

const sc = fc.sample(scenarioArb(), { seed: 3289, numRuns: 44 })[43];
await runScenario(sc);
[s0=1 s1=2 m0=memo(latest(s1) !%4=3) m1=async(latest(s1) !%4=0)]
<span split[s1()]>s0()</span><Show when=m0()><span></span></Show><Loading><span fx[latest(s1),m1()]>m0()</span></Loading>
:: f A(s1=1) s0=1 s1=0 s0=3 s0=3 m S1 A(s0=3)
scenario JSON
{"signals":[1,2],"memos":[{"async":false,"deps":[{"src":{"k":"s","i":1},"how":"latest"}],"poison":3},{"async":true,"deps":[{"src":{"k":"s","i":1},"how":"latest"}],"poison":0}],"tree":{"t":"group","kids":[{"t":"leaf","text":[{"how":"plain","src":{"k":"s","i":0}}],"attrs":[{"how":"plain","src":{"k":"s","i":1}}],"grouped":false},{"t":"show","when":{"how":"plain","src":{"k":"m","i":0}},"child":{"t":"leaf","text":[],"attrs":[],"grouped":true},"fallback":false},{"t":"loading","child":{"t":"leaf","text":[{"how":"plain","src":{"k":"m","i":0}}],"attrs":[{"how":"latest","src":{"k":"s","i":1}},{"how":"plain","src":{"k":"m","i":1}}],"grouped":true}}]},"steps":[{"t":"flush"},{"t":"action","s":1,"v":1},{"t":"write","s":0,"v":1},{"t":"write","s":1,"v":0},{"t":"write","s":0,"v":3},{"t":"write","s":0,"v":3},{"t":"micro"},{"t":"settle","pick":1},{"t":"action","s":0,"v":3}]}

The fx[latest(s1), m1()] render effect reads latest(s1) (held → seated as the hold's verdict reader), then m1 — a verdict-lane derivation whose lane's _parent has been merged (resolveTx(l._parent) !== l._parent) into the very hold the leaf is seated for. Observed: passLane reset, m1's staging served (L7'). The reader-seat table (#4395, row V6/L6) compares holds, not lane objects: no leave, the lane's value.

Found by the S1 reader-seat differential: class V6 expected Lv, got P — slot (V posture; s u d ip vlane parentMerged shown · cU cH cOtherTx t1 w2 vseat oldNoLane flushTx flushNotElTx) plus the downstream verdict seat at the pass's end (old none, new seated), 1 case in the 1500-case regression gate, none in the signals or web suites. The case's screens are all [ROOT-E] (the m0 poison), so nothing visible differs here; the mechanism is wrong independent of the screen.

Why it is a bug

Intended fix

The reader-seat rebuild S1 (#4395) decides the leave from the seat's hold (resolveTx(vhold) !== resolveTx(l._parent)), which fixes this by construction. Until the swap lands, the shadow carries this as a known class (shadowKnown, this issue). #3905 (transaction merging) is the structural fix for lanes keeping pre-merge identities and stays out of scope here.

Activity

  1. ryansolid commented on Oct 30, 2026

    @ryansolid
    MemberAuthor

    Fixed on next by #4455 (131ceb8); it ships in the next RC.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions