@solidjs/signals 2.0.0-rc.13 → 2.0.0-rc.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/dev-shared.js +2059 -3768
  2. package/dist/dev.attribution.js +237 -101
  3. package/dist/dev.js +7466 -8497
  4. package/dist/observe/affects.js +128 -104
  5. package/dist/observe/boundaries.js +492 -665
  6. package/dist/observe/core/action.js +63 -66
  7. package/dist/observe/core/async.js +243 -379
  8. package/dist/observe/core/attribution-queries.js +1 -1
  9. package/dist/observe/core/attribution.js +394 -253
  10. package/dist/observe/core/constants.js +120 -187
  11. package/dist/observe/core/context.js +3 -3
  12. package/dist/observe/core/core.js +874 -1440
  13. package/dist/observe/core/dev.js +3 -3
  14. package/dist/observe/core/effect.js +73 -84
  15. package/dist/observe/core/external.js +5 -5
  16. package/dist/observe/core/graph.js +62 -71
  17. package/dist/observe/core/heap.js +75 -98
  18. package/dist/observe/core/invariants.js +1 -34
  19. package/dist/observe/core/lanes.js +750 -181
  20. package/dist/observe/core/owner.js +80 -101
  21. package/dist/observe/core/scheduler.js +719 -1356
  22. package/dist/observe/core/verdict.js +314 -621
  23. package/dist/observe/flatten.js +79 -0
  24. package/dist/observe/index.js +15 -13
  25. package/dist/observe/map.js +232 -235
  26. package/dist/observe/reveal.js +212 -0
  27. package/dist/observe/signals.js +144 -216
  28. package/dist/observe/store/affects.js +142 -0
  29. package/dist/observe/store/hooks.js +13 -0
  30. package/dist/observe/store/index.js +27 -24
  31. package/dist/observe/store/optimistic.js +571 -0
  32. package/dist/observe/store/projection.js +308 -0
  33. package/dist/observe/store/reconcile.js +259 -0
  34. package/dist/observe/store/store.js +1988 -277
  35. package/dist/observe/store/storePath.js +1 -1
  36. package/dist/{prod/store/next → observe/store}/target.js +17 -18
  37. package/dist/observe/store/types.js +73 -0
  38. package/dist/observe/store/utils.js +1 -13
  39. package/dist/prod/affects.js +130 -106
  40. package/dist/prod/boundaries.js +476 -654
  41. package/dist/prod/core/action.js +71 -74
  42. package/dist/prod/core/async.js +306 -444
  43. package/dist/prod/core/constants.js +117 -186
  44. package/dist/prod/core/context.js +3 -3
  45. package/dist/prod/core/core.js +888 -1462
  46. package/dist/prod/core/effect.js +88 -99
  47. package/dist/prod/core/external.js +5 -5
  48. package/dist/prod/core/graph.js +62 -71
  49. package/dist/prod/core/heap.js +75 -98
  50. package/dist/prod/core/invariants.js +1 -32
  51. package/dist/prod/core/lanes.js +712 -181
  52. package/dist/prod/core/owner.js +75 -96
  53. package/dist/prod/core/scheduler.js +686 -1335
  54. package/dist/prod/core/verdict.js +309 -621
  55. package/dist/prod/flatten.js +77 -0
  56. package/dist/prod/index.js +15 -13
  57. package/dist/prod/map.js +142 -145
  58. package/dist/prod/reveal.js +212 -0
  59. package/dist/prod/signals.js +155 -227
  60. package/dist/prod/store/affects.js +142 -0
  61. package/dist/prod/store/hooks.js +13 -0
  62. package/dist/prod/store/index.js +26 -19
  63. package/dist/prod/store/optimistic.js +566 -0
  64. package/dist/prod/store/projection.js +301 -0
  65. package/dist/prod/store/reconcile.js +259 -0
  66. package/dist/prod/store/store.js +1883 -272
  67. package/dist/prod/store/storePath.js +1 -1
  68. package/dist/{observe/store/next → prod/store}/target.js +13 -17
  69. package/dist/prod/store/types.js +73 -0
  70. package/dist/prod/store/utils.js +1 -11
  71. package/dist/types/affects.d.ts +11 -30
  72. package/dist/types/boundaries.d.ts +66 -176
  73. package/dist/types/carved.d.ts +1 -0
  74. package/dist/types/core/action.d.ts +5 -6
  75. package/dist/types/core/async.d.ts +10 -5
  76. package/dist/types/core/attribution-hooks.d.ts +20 -1
  77. package/dist/types/core/attribution.d.ts +26 -11
  78. package/dist/types/core/constants.d.ts +130 -180
  79. package/dist/types/core/core.d.ts +134 -264
  80. package/dist/types/core/dev.d.ts +2 -2
  81. package/dist/types/core/effect.d.ts +0 -7
  82. package/dist/types/core/graph.d.ts +7 -1
  83. package/dist/types/core/heap.d.ts +5 -7
  84. package/dist/types/core/index.d.ts +5 -5
  85. package/dist/types/core/invariants.d.ts +0 -29
  86. package/dist/types/core/lanes.d.ts +74 -86
  87. package/dist/types/core/owner.d.ts +3 -3
  88. package/dist/types/core/scheduler.d.ts +255 -331
  89. package/dist/types/core/types.d.ts +45 -85
  90. package/dist/types/core/verdict.d.ts +3 -0
  91. package/dist/types/flatten.d.ts +8 -0
  92. package/dist/types/index.d.ts +6 -4
  93. package/dist/types/reveal.d.ts +19 -0
  94. package/dist/types/signals.d.ts +35 -91
  95. package/dist/types/store/affects.d.ts +12 -0
  96. package/dist/types/store/hooks.d.ts +48 -0
  97. package/dist/types/store/index.d.ts +34 -15
  98. package/dist/types/store/optimistic.d.ts +23 -0
  99. package/dist/types/store/projection.d.ts +18 -0
  100. package/dist/types/store/reconcile.d.ts +3 -0
  101. package/dist/types/store/store.d.ts +132 -145
  102. package/dist/types/store/storePath.d.ts +1 -1
  103. package/dist/types/store/target.d.ts +151 -0
  104. package/dist/types/store/types.d.ts +84 -0
  105. package/package.json +1 -1
  106. package/dist/observe/core/optimistic.js +0 -669
  107. package/dist/observe/store/next/optimistic.js +0 -717
  108. package/dist/observe/store/next/projection.js +0 -266
  109. package/dist/observe/store/next/reconcile.js +0 -375
  110. package/dist/observe/store/next/store.js +0 -2608
  111. package/dist/prod/core/optimistic.js +0 -658
  112. package/dist/prod/store/next/optimistic.js +0 -710
  113. package/dist/prod/store/next/projection.js +0 -257
  114. package/dist/prod/store/next/reconcile.js +0 -373
  115. package/dist/prod/store/next/store.js +0 -2484
  116. package/dist/types/core/optimistic.d.ts +0 -6
  117. package/dist/types/store/next/optimistic.d.ts +0 -25
  118. package/dist/types/store/next/projection.d.ts +0 -27
  119. package/dist/types/store/next/reconcile.d.ts +0 -10
  120. package/dist/types/store/next/store.d.ts +0 -145
  121. package/dist/types/store/next/target.d.ts +0 -196
@@ -1,20 +1,16 @@
1
1
  import { releaseFlightTeardown, handleAsync, clearStatus, parkLoadingWindow, notifyStatus, settlePendingSource, settleErroredDependents } from "./async.js";
2
2
 
3
- import { NOT_PENDING, EFFECT_TRACKED, EFFECT_USER, REACTIVE_OPTIMISTIC_DIRTY, CONFIG_DERIVED_OVERRIDE, CONFIG_HELD_CHILDREN, CONFIG_LANE_FRAME, CONFIG_OPTIMISTIC, STATUS_UNINITIALIZED, STATUS_ERROR, STATUS_PENDING, REACTIVE_REASK, REACTIVE_RECOMPUTING_DEPS, REACTIVE_ZOMBIE, CONFIG_SYNC, CONFIG_HAS_LANE, REACTIVE_MISSED_WAKE, REACTIVE_DISPOSED, REACTIVE_SNAPSHOT_STALE, unwrapOverride, CONFIG_DIRECT_COMMIT, CONFIG_HAS_COMPANIONS, CONFIG_PROMOTED, CONFIG_ADOPTED_UNFLUSHED, CONFIG_CHILDREN_FORBIDDEN, CONFIG_FRESH_READ, CONFIG_INPUTS_PUBLISHED, CONFIG_AUTO_DISPOSE, REACTIVE_LAZY, REACTIVE_CHECK, REACTIVE_DIRTY, REACTIVE_IN_HEAP, REACTIVE_IN_HEAP_HEIGHT, REACTIVE_MANUAL_WRITE, CONFIG_AUTHORITATIVE_READ, CONFIG_OVERRIDE_SUPERSEDED, CONFIG_AUTHORITATIVE_OBSERVED, defaultContext, REACTIVE_NONE, CONFIG_HELD_TRUTH, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_NO_SNAPSHOT, CONFIG_IN_SNAPSHOT_SCOPE, NO_SNAPSHOT, CONFIG_HAS_SNAPSHOT, CONFIG_FW_CHILDREN, CONFIG_SLOT_NODE, STORE_SNAPSHOT_PROPS } from "./constants.js";
3
+ import { STATUS_UNINITIALIZED, REACTIVE_LAZY, REACTIVE_NONE, defaultContext, CONFIG_TRANSPARENT, CONFIG_OWNED_WRITE, CONFIG_AUTO_DISPOSE, CONFIG_SYNC, CONFIG_NO_SNAPSHOT, CONFIG_IN_SNAPSHOT_SCOPE, NOT_PENDING, NO_SNAPSHOT, CONFIG_HAS_SNAPSHOT, CONFIG_HELD, REACTIVE_MANUAL_WRITE, REACTIVE_DIRTY, CONFIG_OVERRIDE, REACTIVE_DISPOSED, REACTIVE_CHECK, REACTIVE_IN_HEAP, REACTIVE_REASK, REACTIVE_SNAPSHOT_STALE, REACTIVE_LANE_DIRTY, CONFIG_GUESS, REACTIVE_RECOMPUTING_DEPS, STATUS_PENDING, CONFIG_VERDICT, EFFECT_RENDER, CONFIG_HELD_FRAME, EFFECT_TRACKED, CONFIG_STAGED, STATUS_ERROR, REACTIVE_ZOMBIE, REACTIVE_MISSED_WAKE, REACTIVE_JOINED, REACTIVE_PROBED, REACTIVE_FRAME_READ, REACTIVE_STAGED_READ, REACTIVE_LANE_READ, REACTIVE_SCREEN_READ, REACTIVE_PROBE_UNANSWERED, CONFIG_CHILDREN_FORBIDDEN, CONFIG_PROMOTED, CONFIG_INPUTS_PUBLISHED, CONFIG_FRESH_READ, REACTIVE_IN_HEAP_HEIGHT, CONFIG_SLOT_NODE, EFFECT_USER } from "./constants.js";
4
4
 
5
5
  import { NotReadyError } from "./error.js";
6
6
 
7
7
  import { clearDeps, trimStaleDeps, link, dormantNodes } from "./graph.js";
8
8
 
9
- import { deleteFromHeap, queueFor, insertIntoHeapHeight, enqueueSub, markNode, markHeap, insertIntoHeap } from "./heap.js";
9
+ import { insertIntoHeap, deleteFromHeap, insertIntoHeapHeight, enqueueSub, markNode, markHeap } from "./heap.js";
10
10
 
11
- import { findLane } from "./lanes.js";
11
+ import { inheritId, disposeChildren, linkChild, markDisposal } from "./owner.js";
12
12
 
13
- import "./invariants.js";
14
-
15
- import { disposeChildren, markDisposal, linkChild, inheritId } from "./owner.js";
16
-
17
- import { GlobalQueue, bumpNotifyEpoch, activeTransition, globalQueue, clock, currentTransition, insertSubs, queuePendingNode, wakeParked, heldTrims, runInTransition, schedule, batchJoins, notifyEpoch, dirtyQueue, projectionWriteActive, reaskArmed, armReaskClear } from "./scheduler.js";
13
+ import { schedule, GlobalQueue, clock, txOf, flushTransaction, joinFuture, queuePendingNode, globalQueue, notifyEpoch, insertSubs, dirtyQueue, bumpNotifyEpoch, setPassLane, passLane, joinPassTx, passTx, holdNode, laneDirty, heldTrim, staleReader, stagedReaders, deferZombie } from "./scheduler.js";
18
14
 
19
15
  // The heap's per-node step. A tracked effect's heap visit is its compute
20
16
  // phase — empty, like a user effect whose compute reads nothing — and hands
@@ -24,62 +20,34 @@ import { GlobalQueue, bumpNotifyEpoch, activeTransition, globalQueue, clock, cur
24
20
  // render-effect callback stages its value for the next pass, but a wake pushed
25
21
  // directly into the user queue ran in the SAME pass, read the old value, and
26
22
  // nothing re-notified it when the value landed (#3291).
27
- GlobalQueue.Ke = e => {
28
- if (e.je === EFFECT_TRACKED) {
29
- deleteFromHeap(e, queueFor(e));
30
- e.Xe = true;
31
- e.T.enqueue(EFFECT_USER, e.$e);
32
- } else recompute(e);
23
+ GlobalQueue.Ce = e => {
24
+ if (e.u === EFFECT_TRACKED) {
25
+ deleteFromHeap(e, dirtyQueue);
26
+ e.Ge = true;
27
+ globalQueue.enqueue(EFFECT_USER, e.M);
28
+ }
29
+ // L2: a zombie's fate is the seam's — its owner's commit disposes it, or
30
+ // the park cancels the pass (the write that dirtied it is held). It runs
31
+ // only past a seam that left it alive (a live write to a frame whose
32
+ // owner is held in the future). Its lane's own re-staging is a live write
33
+ // (REACTIVE_LANE_DIRTY — a lane shows ahead of any park): it runs now, as
34
+ // the lane's work, for the reveal this seam (#3463).
35
+ else if (e.R & REACTIVE_ZOMBIE && !(e.R & REACTIVE_LANE_DIRTY)) deferZombie(e); else recompute(e);
33
36
  };
34
37
 
35
- GlobalQueue.en = disposeChildren;
38
+ GlobalQueue.Fe = disposeChildren;
36
39
 
37
40
  let tracking = false;
38
41
 
39
- /** @internal verdict-module glue */ function setPendingCheckActive(e) {
40
- pendingCheckActive = e;
41
- }
42
-
43
- /** @internal verdict-module glue */ function setLatestReadActive(e) {
44
- latestReadActive = e;
45
- }
46
-
47
- /** @internal verdict-module glue */ function setContextInternal(e) {
48
- context = e;
49
- }
50
-
51
- let stale = false;
52
-
53
- let pendingCheckActive = false;
54
-
55
- let latestReadActive = false;
56
-
57
42
  let context = null;
58
43
 
59
- let currentOptimisticLane = null;
60
-
61
- /** Notify `node`'s subscribers on `lane`'s channel: they recompute as the
62
- * lane's passes — a memo publishes a derived override (lanes stage, #3479),
63
- * an effect runs from the lane's queue, at the park, ahead of the
64
- * transaction — the display-ahead view. The write itself is already staged
65
- * (setSignal) and commits with the frame; this walk shows it now. Used by a
66
- * boundary re-armed from a lane pass (boundaries.ts `_swap`, #3540). */ function notifyOnLane(e, n) {
67
- const t = currentOptimisticLane;
68
- currentOptimisticLane = n;
69
- try {
70
- insertSubs(e, true);
71
- } finally {
72
- currentOptimisticLane = t;
73
- }
74
- }
75
-
76
44
  let snapshotCaptureActive = false;
77
45
 
78
46
  let snapshotSources = null;
79
47
 
80
48
  function ownerInSnapshotScope(e) {
81
49
  while (e) {
82
- if (e.nn) return true;
50
+ if (e.he) return true;
83
51
  e = e._parent;
84
52
  }
85
53
  return false;
@@ -91,34 +59,34 @@ function setSnapshotCapture(e) {
91
59
  }
92
60
 
93
61
  function markSnapshotScope(e) {
94
- e.nn = true;
62
+ e.he = true;
95
63
  }
96
64
 
97
65
  function releaseSnapshotScope(e) {
98
- e.nn = false;
66
+ e.he = false;
99
67
  releaseSubtree(e);
100
68
  schedule();
101
69
  }
102
70
 
103
71
  function releaseSubtree(e) {
104
- let n = e.tn;
105
- while (n) {
106
- if (n.nn) {
107
- n = n.un;
72
+ let t = e.Ve;
73
+ while (t) {
74
+ if (t.he) {
75
+ t = t.ge;
108
76
  continue;
109
77
  }
110
- if (n.he) {
111
- const e = n;
112
- e.C &= ~CONFIG_IN_SNAPSHOT_SCOPE;
113
- if (e.oe & REACTIVE_SNAPSHOT_STALE) {
114
- e.oe &= ~REACTIVE_SNAPSHOT_STALE;
115
- e.oe |= REACTIVE_DIRTY;
116
- if (dirtyQueue.ln > e.rn) dirtyQueue.ln = e.rn;
78
+ if (t.ee) {
79
+ const e = t;
80
+ e.i &= ~CONFIG_IN_SNAPSHOT_SCOPE;
81
+ if (e.R & REACTIVE_SNAPSHOT_STALE) {
82
+ e.R &= ~REACTIVE_SNAPSHOT_STALE;
83
+ e.R |= REACTIVE_DIRTY;
84
+ if (dirtyQueue.Ue > e.me) dirtyQueue.Ue = e.me;
117
85
  insertIntoHeap(e, dirtyQueue);
118
86
  }
119
87
  }
120
- releaseSubtree(n);
121
- n = n.un;
88
+ releaseSubtree(t);
89
+ t = t.ge;
122
90
  }
123
91
  }
124
92
 
@@ -129,256 +97,167 @@ function clearSnapshots() {
129
97
  // pre-initialized to undefined (see ext()), and every reader tests
130
98
  // `!== undefined` — assign, don't `delete`: deleting a field pushes the
131
99
  // object to dictionary mode for every later read of every field.
132
- const n = e.o;
133
- if (n != null) n.sn = undefined;
134
- // StoreNode targets share one pre-initialized hidden class (see
135
- // createStoreProxy) — same rule, and only when present so signal-node
136
- // sources don't grow the field.
137
- if (e[STORE_SNAPSHOT_PROPS] !== undefined) e[STORE_SNAPSHOT_PROPS] = undefined;
100
+ const t = e.m;
101
+ if (t != null) t.Le = undefined;
138
102
  }
139
103
  snapshotSources = null;
140
104
  }
141
105
  snapshotCaptureActive = false;
142
106
  }
143
107
 
144
- function recompute(e, n = false) {
108
+ function recompute(e, t = false) {
145
109
  // §12d: any recompute can clean a marked subscriber — invalidate skips.
146
110
  bumpNotifyEpoch();
147
- const t = e.je;
148
- // Lane posture is resolved BEFORE the previous frame is parked below: a
149
- // lane pass parks a lane frame, not a transaction zombie (#3662, #3698;
150
- // see the parking site), so the decision must be known there. `lane` is applied to
151
- // `currentOptimisticLane` further down, once the previous posture is saved.
152
- let i = !!(e.oe & REACTIVE_OPTIMISTIC_DIRTY);
153
- let u = null;
154
- if (i) {
155
- u = GlobalQueue.an(e, true);
156
- // `false` = wake-only lane demotion: recompute plain so a mid-tick
157
- // latest()/isPending() pull stages instead of direct-committing (#3009).
158
- // The predicate lives with the engine (recomputeLane).
159
- if (u === false) i = false;
160
- } else if (e.C & CONFIG_DERIVED_OVERRIDE) {
161
- // Lanes stage (#3479): a pass over a live lane member carrying a derived
162
- // override is the lane's pass whatever channel dirtied it (a boundary
163
- // reset, an unrelated sync write) — its inputs serve the lane's view, so
164
- // its result is the lane's and belongs in the override slot. Run plain,
165
- // A18's sync twin below read that re-derived lane view as a differing
166
- // truth (a fresh array), superseded the override and demoted the lane;
167
- // the lane's next pass then dropped the staged "truth" and left the node
168
- // flagged superseded with nothing to serve (fuzzer latest-1 #2481). A
169
- // demoted node resolves no lane and stays plain: its pass IS the truth.
170
- u = GlobalQueue.an(e, true);
171
- if (u) i = true;
172
- } else if (activeTransition && !n && activeTransition.cn.length) {
173
- // Lane adoption: parent-deeper-than-owned-child can run before its OPT-dirty
174
- // child propagates. Walk deps once and inherit the OPT lane so this node
175
- // recomputes under the right posture and propagates correctly.
176
- u = GlobalQueue.an(e, false);
177
- if (u) i = true;
178
- }
179
- if (!n) {
180
- // A stamped memo re-enters its hold: its value is that transaction's work.
181
- // An effect's pass belongs to whatever dirtied it (A15 corollary: effects
182
- // don't entangle parallel transactions); it joins its stamp only when the
183
- // pass observes the held flight — queue notification (#3407).
184
- if (e.we && !t && activeTransition !== e.we) globalQueue.initTransition(e.we);
185
- deleteFromHeap(e, queueFor(e));
186
- if (e.o !== null) {
187
- e.o.Ce = null;
111
+ const n = e.u;
112
+ // Lanes (plan sec. 28): the seat of a pass is its node's. A node carrying a lane's
113
+ // derived value runs as the lane's whoever dirtied it — a sync write, a
114
+ // boundary reset, a frame rerun — so its children and its result are the
115
+ // lane's; so does a member the lane's own re-staging dirtied
116
+ // (REACTIVE_LANE_DIRTY, lanes.ts — a leaf the lane owns). A written guess's
117
+ // own pass is its truth arriving (A18), the frame's. A first pass is its
118
+ // creator's (ruling A: a lane pass's children are the lane's frame), and
119
+ // under a guess's lane it reads as the lane's too — a binding the lane
120
+ // mounts sees the screen, like the pass that mounted it (#3835). Not
121
+ // under a verdict lane, for its reads or its result: a verdict lane holds
122
+ // verdicts, and a mount it makes is a mainline mount (A29's boundary
123
+ // exemption, #3851). A tracked read of a lane's value moves a
124
+ // derivation's pass into the lane (`read`); a leaf's never moves.
125
+ // Restored at the end, after this pass's staging and runs have been
126
+ // routed.
127
+ const E = passLane;
128
+ setPassLane((e.R & REACTIVE_LANE_DIRTY || (e.i & (CONFIG_OVERRIDE | CONFIG_GUESS)) === CONFIG_OVERRIDE) && e.m?.He?.B ? e.m.He : // Loose `!=`: false with no creator lane (undefined == null).
129
+ t && E?._parent.xe != E && (creatorPass(context)?.R ?? 0) & REACTIVE_RECOMPUTING_DEPS ? E : null);
130
+ // CARVE 2: lane posture resolution (OPTIMISTIC_DIRTY, derived-override
131
+ // re-derivation, lane adoption through deps) went with the optimistic engine.
132
+ if (!t) {
133
+ deleteFromHeap(e, dirtyQueue);
134
+ if (e.m !== null) {
135
+ e.m.Te = null;
188
136
  // Supersede is where an iterator flight dies (#3122): close it now.
189
- // Its cleanup(close) registration may sit in a zombie-deferred
190
- // disposal list that a held transition only drains when the
191
- // SUPERSEDING flight settles — cancellation must not wait for the
192
- // work that replaced it. Idempotent with the cleanup-channel close.
137
+ // Idempotent with the cleanup-channel close.
193
138
  releaseFlightTeardown(e);
194
139
  }
195
- // Tracked effects run after finalizePureQueue, so dispose immediately
196
- // instead of deferring. Children built by an uncommitted recompute
197
- // (CONFIG_HELD_CHILDREN) die immediately too: no frame ever showed them.
198
- // Everything else is the committed frame's. Which frame retires it is
199
- // decided by who owns the pass, never by the node's kind (A15 lane work
200
- // and transaction work, ruled 2026-09-28, #3698). Transaction work — a
201
- // pass under a held transaction — defers them as zombies until this
202
- // node's commit, a transaction-owned node included (#3404): a parked
203
- // node's children predate the hold, and tearing them down when the
204
- // source lands ran cleanups before the transaction's atomic reveal.
205
- // Lane work — a pass over a lane, effect or memo alike — parks a LANE
206
- // frame instead (CONFIG_LANE_FRAME, #3662, #3698): the frame it replaces
207
- // leaves the screen when the lane's queue applies this pass (A30) — not
208
- // at the action's commit — and a held lane defers that with the frame
209
- // still displayed. The drain is the lane's first render entry for this
210
- // pass, pushed before the pass builds the new frame: cleanups before
211
- // side effects — the retired frame's `onCleanup`s run ahead of every
212
- // effect callback of the new frame. While the frame waits, the live
213
- // children were never shown: a superseding pass disposes them here like
214
- // held children, and the parked frame stays. Zombies exist for
215
- // transaction work only: #3662 gated the lane frame on `isEffect`, and a
216
- // memo's lane pass (its value a derived override, A17) parked a
217
- // transaction zombie that made the memo a pending node of the action,
218
- // stamped it, and sent its next mainline recompute through the
219
- // stamped-memo arm above — a `Show` whose `when` getter owns a
220
- // compiler-emitted memo held an unrelated sync write for the action's
221
- // lifetime, against the #3460 ruling that a lane never holds a sync write.
222
- if (t === EFFECT_TRACKED || e.C & (CONFIG_HELD_CHILDREN | CONFIG_LANE_FRAME)) disposeChildren(e); else if (e.tn !== null || e.qe !== null) {
223
- markDisposal(e);
224
- const n = ext(e);
225
- n.fn = e.qe;
226
- n._n = e.tn;
227
- e.qe = null;
228
- e.tn = null;
229
- e.En = 0;
230
- if (u) {
231
- e.C |= CONFIG_LANE_FRAME;
232
- findLane(u).dn[0].push(() => {
233
- e.C &= ~CONFIG_LANE_FRAME;
234
- disposeChildren(e, false, true);
235
- });
140
+ // L2: a pass over a held derivation joins its transaction — the
141
+ // propagation that reached this node reached the transaction, and this
142
+ // pass's answer replaces the held one there. A node held with nothing
143
+ // staged — pending when its flush parked, or a member of a held frame —
144
+ // joins all the same. Not a render effect (rule 3): it is the frame.
145
+ // Re-run outside its transaction's flush it publishes mainline — the
146
+ // value it staged there is superseded (`frameRead` remembers it for the
147
+ // landing). A frame born into the transaction (`bornInto`: never run,
148
+ // A29, or mounted by a pass of it still awaiting its commit, #4010) keeps
149
+ // staging: live writes keep it current, it effects at the swap (ruling
150
+ // A).
151
+ // A pending flight is the transition's own work (#3884). A probe on an
152
+ // earlier pass left CONFIG_VERDICT, and a frame read here would serve the
153
+ // committed screen, settle the flight and drop the hold — a superseding
154
+ // write's verdict would depend on that probe. Cleared for this pass; a
155
+ // probe in the body re-stamps it after the plain reads.
156
+ if (e.S & STATUS_PENDING) e.i &= ~CONFIG_VERDICT;
157
+ if (e.i & CONFIG_HELD) {
158
+ const t = txOf(e);
159
+ // Held by a blocked lane: the frame joins nothing — a lane never holds
160
+ // a sync write (#3460). The pass is the lane's if it reads the lane's
161
+ // world (`read`), and has left it otherwise.
162
+ if (t.B) ; else if (n !== EFFECT_RENDER && !(e.i & CONFIG_VERDICT)) joinPassTx(t); else if (t !== flushTransaction && !(e.S & STATUS_UNINITIALIZED || e.i & CONFIG_HELD_FRAME)) {
163
+ // Published mainline, it is not held: the frame this pass builds is
164
+ // replaced on the spot (#3404) — this flush's commit retires the
165
+ // committed frame the held pass had parked (`commitPendingNode`),
166
+ // after the new frame's first runs, and the held pass's own
167
+ // children die below (`disposeChildren`). The landing re-derives a
168
+ // fresh held frame for the reveal (`_reruns`).
169
+ e.Oe = NOT_PENDING;
170
+ if (n === EFFECT_RENDER) e.i &= ~CONFIG_HELD;
236
171
  }
237
- } else ;
172
+ }
173
+ // The previous pass's frame. If that pass is still awaiting its commit
174
+ // (CONFIG_STAGED) its children were never shown and die on the spot — the
175
+ // frame parked before it stays parked. Otherwise they ARE the frame:
176
+ // park them until this pass commits. Tracked effects run after the seam
177
+ // and own nothing to park.
178
+ if (n === EFFECT_TRACKED || e.i & CONFIG_STAGED) disposeChildren(e); else if (e.Ve !== null || e.Re !== null) parkChildren(e);
238
179
  }
239
- // A derived override (lanes stage, #3479) is override-covered like a written
240
- // one: a plain pass over it — its source superseded (A18) — stages the truth
241
- // and supersedes the override through the sync twin below.
242
- const l = (e.C & (CONFIG_OPTIMISTIC | CONFIG_DERIVED_OVERRIDE)) !== 0 && e.o?.Fe !== NOT_PENDING && e.o?.Fe !== undefined;
243
- const r = !!(e.h & STATUS_UNINITIALIZED);
180
+ const u = !!(e.S & STATUS_UNINITIALIZED);
244
181
  // Capture both error and pending status before the compute clears them.
245
182
  // A conditional can drop its pending source and recover to an unchanged
246
183
  // value, leaving blocked dependents outside that source’s settle walk.
247
- const o = e.h & STATUS_ERROR ? e.o?._ : undefined;
248
- const s = (e.h & STATUS_PENDING) !== 0;
249
- const a = s ? e.o?.ue : undefined;
184
+ const s = e.S & STATUS_ERROR ? e.m?.F : undefined;
185
+ const i = (e.S & STATUS_PENDING) !== 0;
186
+ const l = i ? e.m?.Se : undefined;
250
187
  // Pending SOURCE-hood, captured before the compute clears status: a node
251
188
  // whose own flight parked dependents self-registers in _pendingSources
252
189
  // (notifyStatus, isSource). If this recompute supersedes that flight and
253
190
  // settles synchronously, those dependents settle HERE — asyncWrite's
254
191
  // settlePendingSource walk never runs for a landing that was preempted
255
192
  // (#3181).
256
- const c = e.o?.ue?.has(e);
257
- // Re-ask classification lives in the verdict module; capture the flag before
258
- // the recompute wipes _flags below.
259
- const f = (e.oe & REACTIVE_REASK) !== 0;
260
- // Captured before the compute clears it on a sync landing: if that landing
261
- // is transition-held below, the window must stay open until the hold
262
- // commits (commitPendingNode) — a closed window plus a held value reads as
263
- // a pending frame to live observers of the verdict (#2990).
264
- const _ = e.ve;
265
- // Creation-time A29 (see enterStagedRead): a pass outside a flush that is
266
- // served a live transaction's staged value records the transaction here
267
- // and is staged INTO it below — "born held" — instead of committing.
268
- const E = stagedEntry;
269
- stagedEntry = null;
270
- const d = context;
193
+ const o = e.m?.Se?.has(e);
194
+ // A19 exc. 2: a `refresh()` re-asks the question already answered — quiet
195
+ // for the verdict — unless the question in flight is a new one, which the
196
+ // re-ask does not launder (a poll during a quiet confirm stays quiet).
197
+ const _ = (e.R & REACTIVE_REASK) !== 0 && !(e.S & STATUS_PENDING && e.m !== null && !e.m.ke);
198
+ const r = context;
271
199
  context = e;
272
- e.Nn = null;
273
- e.Ye++;
274
- // REACTIVE_ZOMBIE is position, not scheduling state: it says the node sits
275
- // on its owner's `_pendingFirstChild` chain, and `disposeChildren` keys its
276
- // parent-chain splice off it. A zombie reruns for mainline writes until the
277
- // commit that disposes it (#3463), so the per-pass wipe here — and in the
278
- // finally below and in updateIfNecessary — must carry it (#3543): a
279
- // de-flagged zombie spliced itself out of the LIVE chain at disposal,
280
- // orphaning the owner's current child, which stayed subscribed forever.
281
- e.oe = REACTIVE_RECOMPUTING_DEPS | e.oe & REACTIVE_ZOMBIE;
282
- e.be = clock;
283
- let N = e._e === NOT_PENDING ? e.ce : e._e;
284
- let I = e.rn;
285
- let T = false;
286
- let S = tracking;
287
- let O = currentOptimisticLane;
200
+ e.G = null;
201
+ e.pe++;
202
+ // REACTIVE_ZOMBIE is position: it says the node sits on its owner's parked
203
+ // frame, and the splice/reawaken paths key off it. A zombie reruns for
204
+ // live writes until the commit that disposes it, so every per-pass wipe
205
+ // — here, the finally below, updateIfNecessary — carries it (#3543).
206
+ e.R = REACTIVE_RECOMPUTING_DEPS | e.R & REACTIVE_ZOMBIE;
207
+ e.Ie = clock;
208
+ // The pass's previous value: its staging, else a lane node's lane value
209
+ // when it has one (lanes.ts), else the committed one.
210
+ let I = e.Oe !== NOT_PENDING ? e.Oe : e.i & CONFIG_OVERRIDE && e.m.B !== NOT_PENDING ? e.m.B : e.Ae;
211
+ let a = e.me;
212
+ let N = false;
213
+ // L2: did this pass read the future (REACTIVE_JOINED, set by read)?
214
+ let f = false;
215
+ let T = tracking;
288
216
  tracking = true;
289
- // A computed's fn establishes its OWN dependencies, so it must never run
290
- // inside a latest() read window: read() short-circuits through the
291
- // companion path before dependency linking, so a memo created (eagerly
292
- // computed) inside latest(fn) came out permanently dependency-less (#2926).
293
- // latestRead() already suspends the flag for its pull-recomputes; this
294
- // covers creation-time computes and flushes that run inside the window.
295
- const A = latestReadActive;
296
- latestReadActive = false;
297
- // A memo computes under its OWN lane posture, never the puller's (A31,
298
- // #3442): its value is one shared slot every reader sees, so a pull from a
299
- // lane-carrying reader (a probe effect on its companion lane pulling a sync
300
- // memo) must not run it with that lane's read carve-outs — under a lane, a
301
- // pending node on no lane serves its committed value instead of throwing,
302
- // and the memo then published a stale "settled" value, dropped its
303
- // pending status, and stopped holding its transaction. The branches below
304
- // re-establish the posture the memo itself owns (OPT-dirty, or adopted
305
- // through its deps). Effects keep the ambient lane: their runs are the
306
- // lane's own view.
307
- if (!t) currentOptimisticLane = null;
308
- // Lane posture lives with the engine: OPTIMISTIC_DIRTY is only ever set by
309
- // engine-driven paths, and _optimisticNodes is only pushed by
310
- // _optimisticWrite, so the hook is installed whenever either gate holds.
311
- // (Resolved at the top of this pass, ahead of the parking site.)
312
- if (u) currentOptimisticLane = u;
313
- const C = t && t !== EFFECT_USER;
314
- const p = stale;
315
- if (C) stale = true;
316
- // An effect recorded for this transaction's commit replay (it once read a
317
- // node the transaction held and showed the committed value) and now
318
- // recomputing UNDER the transaction sees its staged view: the value this
319
- // run produces is applied by the commit itself, and the stale recording
320
- // would publish the frame a second time. Drop it; the reads below re-record
321
- // if they are served the committed view again (a lane's committed read).
322
- if (t && activeTransition !== null && activeTransition.In.size) activeTransition.In.delete(e);
323
217
  try {
324
- if (!false && e.C & CONFIG_SYNC) {
325
- N = e.he(N);
326
- if (e.o !== null) e.o.Ce = null;
327
- e.ve = false;
218
+ if (!false && e.i & CONFIG_SYNC) {
219
+ I = e.ee(I);
220
+ if (e.m !== null) e.m.Te = null;
221
+ e.De = false;
328
222
  } else {
329
- // Snapshot `_inFlight` so we can detect whether `_fn` self-registered an async
330
- // subscription (e.g. `createProjection` calls `handleAsync` from inside its body
331
- // with a setter callback). In that case, the outer `handleAsync` call below would
332
- // clobber the fresh subscription, so we skip it and let the internally-registered
333
- // iteration drive updates.
334
- const n = e.o?.Ce;
335
- const t = e.he(N);
336
- const i = typeof t === "object" && t !== null;
337
- const u = e.o?.Ce !== n;
338
- N = u || !i ? t : handleAsync(e, t);
339
- if (!u && !i) {
340
- if (e.o !== null) e.o.Ce = null;
223
+ // A projection's body self-registers its flight through handleAsync
224
+ // (with the commit as setter): its undefined return is not a sync
225
+ // answer, and the outer handleAsync must not clobber the registration.
226
+ const t = e.m?.Te;
227
+ const n = e.ee(I);
228
+ const E = typeof n === "object" && n !== null;
229
+ const u = e.m?.Te !== t;
230
+ I = u || !E ? n : handleAsync(e, n);
231
+ if (!u && !E) {
232
+ if (e.m !== null) e.m.Te = null;
341
233
  // A sync (non-object) return is the first real answer; async-shaped
342
234
  // results clear inside handleAsync at their own landing points, and a
343
- // self-registered flight (inFlightChanged — projections) clears when
344
- // its internal handleAsync lands.
345
- e.ve = false;
235
+ // self-registered flight clears when its own handleAsync lands.
236
+ e.De = false;
346
237
  }
347
238
  }
348
239
  // On a status-free node clearStatus is a guaranteed no-op: every field
349
240
  // its body gates on is either _statusFlags or lives in the cold
350
241
  // extension — no extension, no status to clear. (_x from an unrelated
351
242
  // installer just makes clearStatus a cheap re-verified no-op.)
352
- // A node born held keeps STATUS_UNINITIALIZED until its transaction
353
- // commits: it has no committed value, and read() holds its readers.
354
- if (e.h !== 0 || e.o !== null) clearStatus(e, n && stagedEntry === null);
355
- // _optimisticLane is only ever assigned by engine paths (CONFIG_HAS_LANE
356
- // is their sticky presence mark).
357
- if (e.C & CONFIG_HAS_LANE && e.o?.me) GlobalQueue.Tn(e);
358
- } catch (n) {
359
- const t = n instanceof NotReadyError;
360
- if (t && e.ve) {
243
+ if (e.S !== 0 || e.m !== null) clearStatus(e, t);
244
+ } catch (t) {
245
+ const n = t instanceof NotReadyError;
246
+ if (n && e.De) {
361
247
  // Loading window with an unready sync dependency: register for the
362
248
  // source's settle (the settlePendingSource walk runs off
363
249
  // _pendingSources + _blocked alone) but take NO read-visible pending
364
- // status, no downstream propagation, no transition, no lane
365
- // registration — the committed loading value keeps serving. If the
250
+ // status and no downstream propagation — the committed loading value
251
+ // keeps serving. If the
366
252
  // node is currently errored the error stays the answer until this
367
253
  // retry can actually run.
368
- parkLoadingWindow(e, n);
254
+ parkLoadingWindow(e, t);
369
255
  } else {
370
- // Track pending async in the lane (not the lane's source — it creates the lane
371
- // but doesn't belong to it). Set lane BEFORE notifyStatus for downstream propagation.
372
- if (t && currentOptimisticLane) GlobalQueue.Sn(e);
373
- let i = false;
374
- if (t) {
375
- ext(e).Pe = true;
376
- if (GlobalQueue.On !== null) i = GlobalQueue.On(e, f);
377
- }
378
- notifyStatus(e, t ? STATUS_PENDING : STATUS_ERROR, n, undefined, t ? e.o?.me : undefined);
256
+ if (n) ext(e).de = true;
257
+ notifyStatus(e, n ? STATUS_PENDING : STATUS_ERROR, t);
379
258
  // The replacement source is fully propagated now. If no new flight
380
259
  // re-owned self, retire the superseded flight and its dependent copies.
381
- if (t && c && !e.o?.Ce) settlePendingSource(e);
260
+ if (n && o && !e.m?.Te) settlePendingSource(e);
382
261
  // A re-park drops what the earlier pass carried (#3456): a source this
383
262
  // pass no longer reaches — its branch switched, or a fresh flight
384
263
  // replaced the inputs' pending with its own — stays copied onto
@@ -387,32 +266,34 @@ function recompute(e, n = false) {
387
266
  // dependent then waits forever on a flight it has no path to. The
388
267
  // re-park twin of the unchanged-value recovery sweep below; dependents
389
268
  // with another path keep the source (retryReaches).
390
- if (t && a) for (const n of a) if (n !== e && !e.o?.ue?.has(n)) settlePendingSource(e, n);
391
- if (i) GlobalQueue.k(e);
269
+ if (n && l) for (const t of l) if (t !== e && !e.m?.Se?.has(t)) settlePendingSource(e, t);
392
270
  }
393
271
  } finally {
394
- tracking = S;
395
- latestReadActive = A;
396
- if (C) stale = p;
272
+ tracking = T;
397
273
  // Consume the missed-wake latch (#3037, set by insertSubs): a dep write
398
274
  // landed beneath this pass on a link it had already validated. The wipe
399
275
  // below must not key off DIRTY/CHECK — the read-time pull protocol
400
276
  // (markNode(c) in read()) marks the running node as part of ordinary
401
277
  // bookkeeping, and those marks are correctly discarded here.
402
- T = (e.oe & REACTIVE_MISSED_WAKE) !== 0;
278
+ N = (e.R & REACTIVE_MISSED_WAKE) !== 0;
279
+ f = (e.R & REACTIVE_JOINED) !== 0;
280
+ // A verdict reader is one for as long as it probes: a pass that entered
281
+ // no window (REACTIVE_PROBED) is an ordinary derivation again.
282
+ if (!(e.R & REACTIVE_PROBED)) e.i &= ~CONFIG_VERDICT;
403
283
  // REACTIVE_DISPOSED survives too (#3621): the pass may have disposed its
404
284
  // own owner (a memo calling its root's `dispose()`, a cleanup doing so
405
285
  // #3601/#3606), and `disposeChildren` set the flag on this node
406
286
  // reentrantly. Dropped, the node read as live — `refresh()` re-ran it
407
- // and `isDisposed()` lied.
408
- e.oe = e.oe & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED) | (n ? e.oe & REACTIVE_SNAPSHOT_STALE : 0);
409
- context = d;
287
+ // and `isDisposed()` lied. REACTIVE_FRAME_READ (A15 stale reader) is
288
+ // this pass's verdict for the landing; the next pass's wipe at the top
289
+ // clears it.
290
+ e.R = e.R & (REACTIVE_ZOMBIE | REACTIVE_DISPOSED | REACTIVE_FRAME_READ | REACTIVE_STAGED_READ | REACTIVE_LANE_READ | REACTIVE_SCREEN_READ | REACTIVE_PROBE_UNANSWERED) | (t ? e.R & REACTIVE_SNAPSHOT_STALE : 0);
291
+ context = r;
292
+ // A19 exc. 2: a pass that went pending on a `refresh()` re-asks the
293
+ // question already answered — quiet for the verdict; any other pending
294
+ // pass is a new question. Settled, there is nothing to classify.
295
+ if (e.m !== null) e.m.ke = _ && (e.S & STATUS_PENDING) !== 0;
410
296
  }
411
- // The cast re-widens: TS narrowed `stagedEntry` to `null` at the reset
412
- // above and does not invalidate that across the compute call that
413
- // `enterStagedRead` runs under. No emitted code.
414
- const R = stagedEntry;
415
- stagedEntry = E;
416
297
  // A node that died during its own pass (#3621) is dead at the end of it,
417
298
  // and the pass is void. Its owner's teardown already unlinked its deps,
418
299
  // removed it from the heap and ran its cleanups; what remains is what the
@@ -424,278 +305,192 @@ function recompute(e, n = false) {
424
305
  // so nothing is staged, committed, or propagated to subscribers, and an
425
306
  // effect's run is not enqueued (runEffect would refuse it anyway). The
426
307
  // attribution frame opened at the top is still closed.
427
- if (e.oe & REACTIVE_DISPOSED) {
308
+ if (e.R & REACTIVE_DISPOSED) {
428
309
  clearDeps(e);
429
- if (e.o !== null) e.o.Ce = null;
430
- currentOptimisticLane = O;
310
+ if (e.m !== null) e.m.Te = null;
311
+ setPassLane(E);
431
312
  return;
432
313
  }
433
- if (!e.o?._) {
314
+ // The lane this pass is work of, if any: its seat (the node's, its
315
+ // creator's, or the one a lane read moved it into). A pass in a lane's
316
+ // seat that read none of the lane's world has left it: its result is the
317
+ // frame's (a derivation whose branch no longer reaches the guess). A
318
+ // guess is written, not derived — it never leaves this way.
319
+ let S = passLane;
320
+ // Listed before its staging, a pending pass included (the lane's own
321
+ // flight is the lane's); false: the pass left the lane (lanes.ts).
322
+ const c = !!e.m?.F;
323
+ if (S !== null) {
324
+ if (GlobalQueue.W(e, S, t, c)) setPassLane(S); else S = null;
325
+ }
326
+ if (!e.m?.F) {
434
327
  // INV-11 (#3330): the equality gate compares against the slot this run
435
- // publishes to. An override-covered node publishes the override; a lane
436
- // recompute (OPT-dirty) direct-commits `_value` — the lane's own reveal
437
- // schedule — so a transaction-held `_pendingValue` that already equals
438
- // the new result is not "unchanged": the screen still shows `_value`.
439
- // Only a transaction-staged run compares against `_pendingValue`.
440
- const u = l ? unwrapOverride(e.o?.Fe) : i || e._e === NOT_PENDING ? e.ce : e._e;
441
- let s = false;
328
+ // publishes to — the staged `_pendingValue` when staged; a lane node's
329
+ // lane value (a node entering the lane this pass has none yet — its
330
+ // committed value, `laneStage` having voided a frame staging).
331
+ const i = e.Oe !== NOT_PENDING ? e.Oe : e.i & CONFIG_OVERRIDE && e.m.B !== NOT_PENDING ? e.m.B : e.Ae;
332
+ let _ = false;
442
333
  try {
443
- s = !t && r || !e.xe || !e.xe(u, N);
444
- } catch (n) {
334
+ _ = !n && u || !e.ye || !e.ye(i, I);
335
+ } catch (t) {
445
336
  // A throwing user comparator is an error of this node's computation.
446
337
  // Route it through the same status path as a compute-phase throw so
447
338
  // error boundaries contain it; otherwise it unwinds the scheduler
448
339
  // flush, bypassing every boundary and wedging the queue (#2837).
449
- notifyStatus(e, STATUS_ERROR, n);
340
+ notifyStatus(e, STATUS_ERROR, t);
450
341
  }
451
342
  // Effects use `_equals: false` (no per-effect closure). The side effects that
452
343
  // the equals closure used to perform — flagging the effect dirty and enqueueing
453
344
  // its runner — happen here instead. `!create` matches the previous `initialized`
454
345
  // gate: the explicit recompute(node, true) inside effect() does not enqueue, so
455
346
  // effect() can call its runner synchronously for the first run.
456
- if (t && s) {
457
- e.Xe = !e.o?._;
347
+ if (n && _ &&
348
+ // A stale reader of a lane (REACTIVE_SCREEN_READ) that computed what it
349
+ // last applied owes no run (lanes.ts).
350
+ !(e.R & REACTIVE_SCREEN_READ && !e.Ge && Object.is(e.be, I)) &&
351
+ // A re-staging pass owes no run: the commit replays the effect (#3802).
352
+ e.Oe === NOT_PENDING) {
353
+ e.Ge = !e.m?.F;
458
354
  // Reuse one bound runner per effect — runEffect no-ops on a stale
459
355
  // `_modified`, so re-enqueueing the same function is harmless.
460
- if (!n) {
461
- e.T.enqueue(t, e.An ??= GlobalQueue.Cn.bind(null, e));
462
- // Contested effect (#3322). Effects don't entangle transactions (a
463
- // shared effect is not shared state), yet they have one value slot:
464
- // when this write commits under a different transaction
465
- // (`activeTransition`, null = mainline) than the one that produced the
466
- // previous value (`_valueTransition`), that view is gone. Rather than
467
- // merge the two, each commit re-derives the effect against its own
468
- // committed world (Transition._contested, re-dirtied by
469
- // finalizePureQueue ahead of the heap run). The previous owner always
470
- // needs it if still live: its commit is silent (staging already
471
- // notified) and the value it computed is gone. The new owner needs it
472
- // too, for the same reason, unless it is mainline — mainline publishes
473
- // what it computes. A previous owner that was mainline, or already
474
- // committed, left nothing to protect.
475
- let n = e.pn;
476
- if (n !== activeTransition) {
477
- e.pn = activeTransition;
478
- if (n !== null && (n = currentTransition(n)) !== activeTransition && !n.Rn) {
479
- (n.Dn ??= []).push(e);
480
- if (activeTransition !== null) (activeTransition.Dn ??= []).push(e);
481
- }
482
- }
483
- }
356
+ // Lane work's run is the lane's (released at its reveal) — a first
357
+ // pass under a lane too: `effect()` skips the synchronous first run of
358
+ // a lane's node.
359
+ if (!t || S !== null) globalQueue.enqueue(n, e.Y ??= GlobalQueue.q.bind(null, e), S);
484
360
  }
485
- if (e.o?._) ; else if (s) {
486
- const u = l ? e.o?.Fe : undefined;
487
- if (n && R === null ||
488
- // Plain sync flush (no transition on either side) commits effect
489
- // values directly — the pending round-trip (queuePendingNode +
490
- // commitPendingNodes) exists to sequence transition reveals, and
491
- // paying it per effect on the plain path is pure overhead.
492
- // DIRECT_COMMIT effects (resolve/until) commit directly even under
493
- // their own held transition: their applies deliver on a microtask,
494
- // not the stashed queues, so a staged value would hand the immediate
495
- // apply stale state — see CONFIG_DIRECT_COMMIT.
496
- t && R === null && (activeTransition !== e.we || activeTransition === null || e.C & CONFIG_DIRECT_COMMIT) || i) {
497
- // Lanes stage (#3479): a lane pass on a memo publishes its speculative
498
- // result as an OVERRIDE — `_value` stays the committed truth, so a
499
- // reader off the lane (A17's committed view, #3460) sees a whole
500
- // committed frame: the source's shadow and its derivations together,
501
- // never a committed shadow beside a speculative memo. The lane's own
502
- // readers and untracked reads see the override (A17); the revert
503
- // drops it and re-derives (resolveOptimisticNodes). Effects keep the
504
- // direct commit — their `_value` is a run result the lane's queues
505
- // already sequence. Either way the lane pass drops any superseded
506
- // older hold so its queued commit can't clobber the fresh frame: a
507
- // hold staged on an earlier, lane-free pass of the SAME transaction
508
- // is superseded — left in place, the commit published the older
509
- // frame over the fresh one (#3377). A reversion pass (OPT-dirty with
510
- // no lane — the override dropped) commits directly: it IS the truth.
511
- if (i && !t && currentOptimisticLane !== null) GlobalQueue.Me(e, N, currentOptimisticLane); else e.ce = N;
512
- if (i) e._e = NOT_PENDING;
361
+ // Lanes. A written guess's own pass from the frame: its source
362
+ // recomputed it — the truth (A18: "the source is whatever recomputes the
363
+ // node"). It stages under the lane's parent whether or not it changed (a
364
+ // confirm is a landing too — the lane's end commits it) and notifies
365
+ // only a correction: an equal truth re-runs nothing (lanes.ts). (As the
366
+ // lane's own work the lane judged it — `laneStage`.)
367
+ if (!t && S === null && e.i & CONFIG_GUESS && GlobalQueue.We(e, I, c)) ; else if (c) ; else if (_) {
368
+ // L2: a first pass is born held — staged, committed with the
369
+ // transaction, its effect's first run the landing's (A29; `effect()`
370
+ // skips the synchronous first run on a staged value) — when the flush
371
+ // has joined a transaction and either the pass read a held node (it
372
+ // derives from that world — the join merged the node's transaction
373
+ // into the flush's), or — a verdict lane's mount, mainline — read a
374
+ // staging of this flush (the same world before the seam parks it; the
375
+ // mount is not shown ahead of it: #3851), or a pass created it (ruling
376
+ // A: a held pass's children are the transaction's). Any other mount
377
+ // that read a staging does not join by being new (2026-10-07): it
378
+ // publishes and re-derives if the flush parks. A node created outside any pass
379
+ // that read only the committed world (root setup, a mount, an effect
380
+ // callback) is nobody's frame and publishes directly, as does a first
381
+ // pass that runs before anything joins: the pass's input, not a verdict
382
+ // after it. An effect still carrying an uncommitted staged value
383
+ // re-stages: the commit applies the latest pass, not the born-held one.
384
+ if (S !== null) {
385
+ // Lane work: the lane's value — into the slot while the lane has not
386
+ // revealed (its reveal shows it), as its staging once it has (the
387
+ // next reveal promotes it; the screen keeps the revealed value). An
388
+ // effect's value slot is private: the run the lane holds is what
389
+ // shows it — unless the effect was born held, whose first run is the
390
+ // commit's (A29): it re-stages, as below.
391
+ if (n && e.Oe === NOT_PENDING) e.Ae = I; else if (n || S.Ze) e.Oe = I; else e.m.B = I;
392
+ } else if (t ? !((flushTransaction !== null || passTx !== null) && (f || E && e.R & REACTIVE_STAGED_READ || (creatorPass(r)?.R ?? 0) & REACTIVE_JOINED)) : n && e.Oe === NOT_PENDING) {
393
+ // A first pass publishes directly. So does an effect: its value slot
394
+ // is private (only its own run reads it), and the staging round-trip
395
+ // (queuePendingNode + commitPendingNodes) paid per effect on the
396
+ // plain path is pure overhead.
397
+ // NOTE (stage-3, 2026-08-21): a quiet-world MEMO direct-commit was
398
+ // attempted here and REVERTED — memo staging is load-bearing beyond
399
+ // transitions: mid-batch pulls (read-triggered recomputes before
400
+ // sources commit) must see the fresh value while PLAIN reads stay
401
+ // committed until flush (#3009 purity). The pending round-trip is
402
+ // that separation; it cannot be skipped on any path a pull can reach.
403
+ e.Ae = I;
513
404
  } else {
514
- e._e = N;
515
- if (R !== null) {
516
- // Born held: the pass ran from mainline and derived from this
517
- // transaction's staged world (enterStagedRead). Its value is the
518
- // transaction's — staged into it directly, stamped, and committed
519
- // with it; mainline's batch never sees it. A born-held effect
520
- // skips its synchronous first run (effect()) and is replayed by
521
- // the commit like a stale reader that showed the committed frame.
522
- // (A re-pass under a fresh boundary — the in-flush form — is
523
- // already the transaction's: restaged, not re-queued.)
524
- if (e.we !== R) {
525
- e.we = R;
526
- R.Gn.push(e);
527
- }
528
- if (t) R.In.add(e);
529
- // Under a boundary that has not revealed, a held first value is
530
- // something not ready under it (#3540): the boundary collects this
531
- // node as a source and shows its fallback; the source stays
532
- // collected while born held (CollectionQueue._checkSources) and
533
- // is released by the commit that initializes it. A boundary that
534
- // already shows content is not told — it holds like any reader.
535
- if (underFreshLoadingBoundary(e)) e.T.notify(e, STATUS_PENDING, STATUS_PENDING, new NotReadyError(e));
405
+ e.Oe = I;
406
+ // Born held: in the transaction from birth, so a read of it this
407
+ // tick is a read of its world (the mount-during-a-hold case), not
408
+ // only after the seam parks it.
409
+ if (t) {
410
+ holdNode(e, flushTransaction ?? passTx);
411
+ // Born into the future: no committed value until the landing
412
+ // (`commitPendingNode` initializes it) — every reader of it
413
+ // derives from the future (`read`), an untracked one throws
414
+ // (A19 exc. 1).
415
+ e.S |= STATUS_UNINITIALIZED;
416
+ // A boundary that has not shown content catches it: its fallback
417
+ // waits for the commit (#3540). (The pass's own status clear above
418
+ // dropped what the read recorded.)
419
+ GlobalQueue.fe?.(e);
536
420
  }
537
- // A window landing that gets held re-opens the window until the hold
538
- // commits — the verdict's held-value branch is window-gated (#2990).
539
- if (_) e.ve = true;
540
- // A staged sync recompute is a write path like setSignal/asyncWrite,
541
- // so sync derivations of held sources stay visible to isPending()/latest()
542
- // (#2831). Both companion writes are transition-scoped (optimistic) and
543
- // auto-revert/re-derive at commit. Not gated on an active transition:
544
- // a plain flush can still become a hold after this recompute — an
545
- // async memo downstream pends and the batch is adopted into a
546
- // transaction (scheduler.enterTransition) — and nothing re-derives
547
- // the companion at adoption, so a memo held that way read
548
- // isPending() false while its held source read true (#3413).
549
- if (e.C & CONFIG_HAS_COMPANIONS && GlobalQueue.Ve !== null) GlobalQueue.Ve(e, N);
550
421
  }
551
422
  // insertSubs only walks _subs (no scheduling of its own), so a
552
423
  // subscriber-less node has nothing to notify.
553
- if (e.u !== null && (!l || i || e.o?.Fe !== u)) insertSubs(e, i || l);
554
- // A18 supersession, sync twin of asyncWrite's override branch (#3331):
555
- // this pass published truth that differs from the override (the gate
556
- // compared against it) into the transaction-held slot. Whether the
557
- // source is this node's own async or an upstream node it derives from
558
- // synchronously makes no difference — "the source recomputed". The
559
- // override stays displayed until the commit; the graph moves to the
560
- // staged truth now (plain channel, lane demoted). Ordering: "a new
561
- // value from the source" postdates the override — an override written
562
- // in this same tick (optimisticWrite stamps `_overrideTime`) is the
563
- // newer intent over whatever this pass derives from the batch's staged
564
- // inputs, and is not superseded by it.
565
- else if (l && !i && e.o.hn !== clock) GlobalQueue.ke(e, N);
566
- } else if (l) {
567
- // Unchanged value (equals the override) recomputed while the override
568
- // is active: _value may still be stale, so hold the authoritative value
569
- // for commit on its own transition's schedule — invisibly (A17/A18).
570
- if (e._e === NOT_PENDING) queuePendingNode(e);
571
- e._e = N;
572
- if (_) e.ve = true;
573
- // see the held branch above (#2990)
574
- // A confirmation after a supersession restores the override as the
575
- // graph's value and notifies; a plain confirmation wakes only an
576
- // authoritative-view reader (until()'s predicate, refresh()'s waiter)
577
- // that observed this node past its override — "authoritative arrival
578
- // equal to the override" is exactly the acknowledgment it waits for;
579
- // A17 silence holds for every ordinary subscriber. Both live in the
580
- // engine's supersedeOverride.
581
- GlobalQueue.ke(e, N);
582
- } else if (e.rn != I) {
583
- for (let n = e.u; n !== null; n = n.Ae) {
584
- insertIntoHeapHeight(n.ge, queueFor(n.ge));
424
+ if (e.t !== null) {
425
+ insertSubs(e);
426
+ // Lane work re-staged: the lane's members re-derive as its work.
427
+ if (S !== null) laneDirty(e, S);
585
428
  }
429
+ } else if (e.me != a) {
430
+ for (let t = e.t; t !== null; t = t.o) insertIntoHeapHeight(t.l, dirtyQueue);
586
431
  }
587
432
  // Silent recovery: errored → unchanged value fires no notification, but
588
433
  // dependents still holding the propagated error consumed their dirty flag
589
434
  // in an errored run and may sit on stale commits (#2949). Changed-value
590
435
  // recoveries ride insertSubs above; a comparator throw re-errored the node
591
436
  // (el._x?._error re-set), so this only runs on a genuinely clean recovery.
592
- if (!s && !e.o?._) {
593
- if (o !== undefined) settleErroredDependents(e, o);
437
+ if (!_ && !e.m?.F) {
438
+ if (s !== undefined) settleErroredDependents(e, s);
594
439
  // Self-registration (this node's own superseded flight) is the #3181
595
440
  // sweep's business below — retiring it here too would walk twice.
596
- if (a) for (const n of a) if (n !== e) settlePendingSource(e, n);
441
+ if (l) for (const t of l) if (t !== e) settlePendingSource(e, t);
597
442
  }
598
443
  // #3181: a synchronous settle supersedes the old landing callback, so
599
444
  // recompute owns its pending-source sweep. An uninitialized node without
600
445
  // a replacement source still has no truth to reveal and must stay parked.
601
- if (c && !(e.h & (STATUS_PENDING | STATUS_UNINITIALIZED))) {
602
- settlePendingSource(e);
603
- // The superseded flight will not enter its waiting transactions at
604
- // landing. Recheck them now so an unchanged value cannot leave their
605
- // writes parked after the last pending source has settled.
606
- wakeParked();
607
- }
446
+ if (o && !(e.S & (STATUS_PENDING | STATUS_UNINITIALIZED))) settlePendingSource(e);
447
+ }
448
+ // A first pass that went pending where the born-held arm would have held
449
+ // its value (it read a held node, or a verdict lane's mount read a staging,
450
+ // #3851) is held like it: its answer is the hold's, and the hold waits
451
+ // for it (A29, #3800) — unless a boundary that has not shown content
452
+ // catches it: then it is the boundary's, and the hold does not wait (#3540).
453
+ if (t && S === null && (f || E && e.R & REACTIVE_STAGED_READ) && e.S & STATUS_PENDING) {
454
+ const t = flushTransaction ?? passTx;
455
+ if (t !== null && !t.B && !GlobalQueue.fe?.(e)) holdNode(e, t);
608
456
  }
609
- // A REPORTER whose pass stopped reading a source it reported on (a gate
610
- // closed) stops counting for the transaction waiting on it (A15 / #3426:
611
- // the hold lasts while a live reporter observes the flight). Nothing else
612
- // re-judges a parked transaction (see wokenTransitions; the disposal
613
- // (#3372) and boundary (#3375) twins of this site), so the writes it held
614
- // stayed staged for as long as the flight stayed up — forever, for one
615
- // that never lands (fuzzer #3446 P1, spec O3). The event is "this pass
616
- // dropped a dep" — deps past `_depsTail` (trimmed below, or kept by A30
617
- // for a staged pass), or a pass that read nothing — not "recovered from
618
- // pending": a reporter registered by the stale-reader carve-out
619
- // (heldFromStale, an INITIALIZED source refetching) displays the committed
620
- // value and is never pending (fuzzer case 79). Every parked transaction,
621
- // not the reporter's stamp: the transaction waiting on it registered it
622
- // without stamping it. One idle pass per parked transaction; done ones
623
- // return at re-entry. Effects only — reporters register from render-effect
624
- // notification (INV-3).
625
- // (`_depsTail` was reset at the top of the pass; TS keeps that narrowing.)
626
- const D = e.Nn;
627
- if (t && (s && !(e.h & STATUS_PENDING) || (D === null ? e.Ie !== null : D.Ne !== null))) wakeParked();
628
- // Dependencies are the committed frame's until it is replaced (A30, #3410; the
629
- // deps twin of the held children above): a pass that staged its value
630
- // leaves the previous pass's tail linked for `commitPendingNode` to trim,
631
- // so a write to a dependency the committed value still derives from
632
- // reaches this node — and joins its hold if the stage is transaction-held
633
- // by then (a plain flush decides nothing here: the transaction that holds
634
- // the pass may open later in the same flush). A pass that published
635
- // directly (a first pass, a lane's own reveal) trims now. An errored pass
636
- // (a throw, NotReady included, or a comparator throw above) keeps its full
457
+ // Dependencies are the committed frame's until it is replaced (A30, #3410):
458
+ // a pass that staged its value leaves the previous pass's tail linked for
459
+ // `commitPendingNode` to trim, so a write to a dependency the committed
460
+ // value still derives from reaches this node until the commit. A pass that
461
+ // published directly, or changed nothing, trims now. An errored pass (a
462
+ // throw, NotReady included, or a comparator throw above) keeps its full
637
463
  // list as before — `_depsTail` marks where it stopped — and the commit
638
464
  // skips it by the same `_error`. An effect's frame is the run its value is
639
- // applied by, not the value slot (#3438): a direct-committed pass that
640
- // still owes a run (`_modified`) has not replaced what the last run
641
- // published — the same flush may stash that run into a transaction it
642
- // opens later — so its tail waits for `runEffect` to trim once the run
643
- // applies. A pass that changed nothing replaced nothing either (#3469): the
644
- // same flush may park with its inputs held, and the committed frame still
645
- // derives from the tail — its trim waits on the flush's verdict (heldTrims).
646
- // A tracked effect's pass IS its run (it bypasses the heap and runs after
647
- // the commit): the frame is replaced, trim now.
648
- if (!e.o?._ && e._e === NOT_PENDING && !(t && e.Xe)) {
649
- if (n || i || t === EFFECT_TRACKED) trimStaleDeps(e); else if (e.Nn?.Ne ?? e.Ie) heldTrims.push(e);
650
- }
651
- currentOptimisticLane = O;
652
- // A parked LANE frame is not a hold (#3662): its drain is the effect's own
653
- // run, not a commit — the node is neither queued nor stamped for it, and
654
- // the release below (for transaction zombies) leaves it parked.
655
- const G = (e.C & CONFIG_LANE_FRAME) !== 0;
656
- const h = e._e !== NOT_PENDING || !G && e.o !== null && (e.o._n !== null || e.o.fn !== null) || (e.h & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0;
657
- // Override-covered holds (hasOverride) always queue: their commit belongs
658
- // to their own transition's schedule (A18 re-rule) and is unobservable
659
- // under the override (A17). Revert no longer commits anything, so an
660
- // unqueued covered hold would leak (INV-7) once the revert clears
661
- // _transition.
662
-
663
- // While a pass's result waits on a commit, its children (and `_disposal`)
664
- // wait with it, and a re-run may tear them down immediately
665
- // (CONFIG_HELD_CHILDREN, #3404). One exception: a transaction-owned effect
666
- // recomputed mainline (contested, #3322) published its value directly — a
667
- // `_pendingValue` left from an earlier held pass is that transaction's,
668
- // not this one's — so this pass's children are the frame's, and any
669
- // zombies deferred at the top are superseded on that same frame. Its
670
- // commit rides the transaction, not this flush: release them here rather
671
- // than let two generations render at once. A LANE pass is the same case
672
- // (#3662): it direct-commits ahead of its transaction, so its children are
673
- // the frame's too. Left flagged for an OLDER frame's zombies, the stamped
674
- // transaction's same-flush re-run below disposed the lane's freshly built
675
- // children (an insert's inner effect, its run still queued in the lane)
676
- // and held their replacements for a commit that never came.
677
- let P = h && (!n || R !== null || (e.h & STATUS_PENDING) !== 0);
678
- if (P && (!e.we || l)) queuePendingNode(e); else if (P && (activeTransition === null || i) && !(e.h & (STATUS_PENDING | STATUS_UNINITIALIZED))) {
679
- P = false;
680
- if (!G) disposeChildren(e, false, true);
465
+ // applied by, not the value slot (#3438): a pass that still owes a run
466
+ // (`_modified`) has not replaced what the last run published, so its tail
467
+ // waits for `runEffect` to trim once the run applies. A pass that changed
468
+ // nothing replaced nothing either (#3469): it cannot know at its tail
469
+ // whether the flush that ran it will park with its inputs held, so the
470
+ // trim waits on the flush's verdict (`heldTrim`: trimmed at the commit,
471
+ // kept at a park — one spurious recompute at most). A creation pass and a
472
+ // tracked effect trim now: their frames are replaceable like a direct
473
+ // commit, and a tracked effect's spurious run would be user-visible.
474
+ if (!e.m?.F && e.Oe === NOT_PENDING && !(n && e.Ge)) {
475
+ if (t || n === EFFECT_TRACKED) trimStaleDeps(e); else heldTrim(e);
681
476
  }
682
- if (P) e.C |= CONFIG_HELD_CHILDREN; else e.C &= ~CONFIG_HELD_CHILDREN;
683
- // (A born-held pass IS the transaction's staged view — nothing to refresh.)
684
- if (e.we && t && activeTransition !== e.we && R === null) {
685
- // The re-run refreshes the transaction's STAGED view (_pendingValue); the
686
- // value this pass published in _value belongs to the run that just
687
- // finished. Keep that ownership, or the effect phase parks a
688
- // mainline-computed value with the transaction (#3412).
689
- const n = e.pn;
690
- runInTransition(e.we, () => recompute(e));
691
- e.pn = n;
477
+ // A staged value, a parked frame (L2: the commit retires it), or status the
478
+ // commit sweep must settle (a pending or uninitialized pass), queues the
479
+ // node for this flush's commit. A first pass queues only when pending or
480
+ // born held: otherwise its value published directly. A queued pass's
481
+ // children are uncommitted (CONFIG_STAGED) until that commit.
482
+ if (e.Oe !== NOT_PENDING || e.m !== null && (e.m.Qe !== null || e.m.ve !== null) || (e.S & (STATUS_PENDING | STATUS_UNINITIALIZED)) !== 0 && (!t || (e.S & STATUS_PENDING) !== 0)) {
483
+ e.i |= CONFIG_STAGED;
484
+ // Lane work is the lane's to reveal (its seam), not this flush's commit.
485
+ if (S === null) queuePendingNode(e);
692
486
  }
487
+ setPassLane(E);
693
488
  // Missed-wake reschedule (see the finally above): values this pass read
694
489
  // before the nested commit are stale, so run again now that the heap will
695
490
  // accept the node. Equality gates stop same-value landings from cascading,
696
491
  // and a re-run only latches again if another nested commit changes a dep
697
492
  // beneath it — convergent unless deps genuinely keep changing.
698
- if (T) {
493
+ if (N) {
699
494
  enqueueSub(e);
700
495
  schedule();
701
496
  }
@@ -708,38 +503,38 @@ function updateIfNecessary(e) {
708
503
  // (#3037); readers meanwhile serve the values the pass has so far.
709
504
  // Never recompute a DISPOSED node either: recompute rewrites _flags and
710
505
  // would resurrect it (#2983) — readers serve its last value.
711
- if (e.oe & (REACTIVE_RECOMPUTING_DEPS | REACTIVE_DISPOSED)) return;
712
- if (e.oe & REACTIVE_CHECK) {
713
- for (let n = e.Ie; n; n = n.Ne) {
714
- const t = n.Oe;
715
- const i = t.De || t;
716
- if (i.he) {
717
- updateIfNecessary(i);
506
+ if (e.R & (REACTIVE_RECOMPUTING_DEPS | REACTIVE_DISPOSED)) return;
507
+ if (e.R & REACTIVE_CHECK) {
508
+ for (let t = e.p; t; t = t._) {
509
+ const n = t.I;
510
+ if (n.ee) {
511
+ updateIfNecessary(n);
718
512
  }
719
- if (e.oe & REACTIVE_DIRTY) {
513
+ if (e.R & REACTIVE_DIRTY) {
720
514
  break;
721
515
  }
722
516
  }
723
517
  }
724
- if (e.oe & (REACTIVE_DIRTY | REACTIVE_OPTIMISTIC_DIRTY) || e.o?._ && e.be < clock && !e.o?.Ce) {
518
+ if (e.R & REACTIVE_DIRTY || e.m?.F && e.Ie < clock && !e.m?.Te) {
725
519
  recompute(e);
726
520
  }
727
521
  // The guard above refused an already-disposed node; the recompute it just
728
522
  // ran may have disposed it (#3621) — carry the flag, or it comes back alive.
729
- // The manual-write mask is state, not scheduling (#3612): it says the
730
- // node's staging is a PROPOSAL, and only the commit (or a later-tick
731
- // refresh, #3026) lifts it — a pull that recomputed nothing must not, or
732
- // an isPending()/latest() probe decided whether a later write to a held
733
- // node was a second proposal or the derivation's `prev`.
734
- e.oe = e.oe & (REACTIVE_SNAPSHOT_STALE | REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT | REACTIVE_ZOMBIE | REACTIVE_DISPOSED | REACTIVE_MANUAL_WRITE);
523
+ // The manual-write mark is state, not scheduling (#3612, A34 rule B): it
524
+ // says the node's staging is a PROPOSAL, lifted by a pass that re-derives
525
+ // or by the commit — a pull that recomputed nothing must not. Nor may it
526
+ // erase the last pass's verdicts for the landing (a stale reader's
527
+ // REACTIVE_FRAME_READ — a verdict reader pulled by a sibling before the
528
+ // landing lost its re-derivation; the lane reads likewise).
529
+ e.R = e.R & (REACTIVE_SNAPSHOT_STALE | REACTIVE_IN_HEAP | REACTIVE_IN_HEAP_HEIGHT | REACTIVE_ZOMBIE | REACTIVE_DISPOSED | REACTIVE_MANUAL_WRITE | REACTIVE_FRAME_READ | REACTIVE_STAGED_READ | REACTIVE_LANE_READ | REACTIVE_SCREEN_READ | REACTIVE_PROBE_UNANSWERED);
735
530
  }
736
531
 
737
- function computed(e, n) {
738
- const t = n?.transparent ?? false;
532
+ function computed(e, t) {
533
+ const n = t?.transparent ?? false;
739
534
  // `in` (not `!== undefined`): an explicit `loadingValue: undefined` on a
740
535
  // `T | undefined` node is a real commit #0. The typeof guard tolerates
741
536
  // non-object option values that older call shapes force through `as any`.
742
- const i = n !== null && typeof n === "object" && "loadingValue" in n;
537
+ const E = t !== null && typeof t === "object" && "loadingValue" in t;
743
538
  // Two literals, one per tier, selected at build time (the observe flag is
744
539
  // a literal after replacement; the untaken branch is dead code). The observe
745
540
  // literal is the prod literal plus its `_name` slot — a slot in the
@@ -749,43 +544,40 @@ function computed(e, n) {
749
544
  // two in sync — the dist artifact test pins observe's key set to prod's
750
545
  // plus `_name`.
751
546
  const u = {
752
- id: inheritId(n, t, context),
753
- C: (t ? CONFIG_TRANSPARENT : 0) | (n?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || n?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (n?.sync ? CONFIG_SYNC : 0) | (n?.H ? CONFIG_NO_SNAPSHOT : 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
754
- xe: n?.equals ?? isEqual,
755
- qe: null,
756
- T: context?.T ?? globalQueue,
757
- Je: context?.Je ?? defaultContext,
758
- En: 0,
759
- he: e,
760
- ce: i ? n.loadingValue : undefined,
761
- rn: 0,
762
- Pn: undefined,
763
- Fn: null,
764
- Ie: null,
765
- Nn: null,
766
- Ye: 0,
767
- u: null,
768
- mn: null,
547
+ id: inheritId(t, n, context),
548
+ i: (n ? CONFIG_TRANSPARENT : 0) | (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (!context || t?.lazy ? CONFIG_AUTO_DISPOSE : 0) | (t?.sync ? CONFIG_SYNC : 0) | (t?.ae ? CONFIG_NO_SNAPSHOT : 0) | (t?.Me ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
549
+ ye: t?.equals ?? isEqual,
550
+ Re: null,
551
+ D: context?.D ?? defaultContext,
552
+ Be: 0,
553
+ ee: e,
554
+ Ae: E ? t.loadingValue : undefined,
555
+ me: 0,
556
+ je: undefined,
557
+ we: null,
558
+ p: null,
559
+ G: null,
560
+ pe: 0,
561
+ t: null,
562
+ Ye: null,
769
563
  _parent: context,
770
- un: null,
771
- gn: null,
772
- tn: null,
773
- oe: n?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
564
+ ge: null,
565
+ Ke: null,
566
+ Ve: null,
567
+ R: t?.lazy ? REACTIVE_LAZY : REACTIVE_NONE,
774
568
  // A loadingValue node is born committed: commit #0 is already in _value.
775
- h: i ? 0 : STATUS_UNINITIALIZED,
776
- be: clock,
777
- _e: NOT_PENDING,
778
- we: null,
779
- vn: -1,
780
- ve: i,
781
- // Cold machinery (async/transition/optimistic/verdict slots) lives one
782
- // hop away in the lazily-allocated extension — the core literal MUST
569
+ S: E ? 0 : STATUS_UNINITIALIZED,
570
+ Ie: clock,
571
+ Oe: NOT_PENDING,
572
+ qe: -1,
573
+ De: E,
574
+ // Cold machinery (async slots) lives one hop away in the lazily-allocated extension — the core literal MUST
783
575
  // stay under V8's in-object boundary (§12: past ~39 fields every
784
576
  // allocation spills to a backing store and creation cost ~4x's).
785
- o: null
577
+ m: null
786
578
  };
787
- if (n?.unobserved) ext(u).Hn = n.unobserved;
788
- setupComputedNode(u, n);
579
+ if (t?.unobserved) ext(u).Je = t.unobserved;
580
+ setupComputedNode(u, t);
789
581
  return u;
790
582
  }
791
583
 
@@ -793,269 +585,286 @@ function computed(e, n) {
793
585
  * computeds — `_x` access stays monomorphic). Installers write through
794
586
  * this; hot paths read `el._x?._field` gated by the _config presence bits.
795
587
  * Never call ext() just to store a field's default. */ function ext(e) {
796
- return e.o ??= {
797
- Fe: undefined,
798
- Ln: undefined,
799
- hn: 0,
800
- bn: NOT_PENDING,
801
- Vn: 0,
802
- me: undefined,
803
- Ze: undefined,
804
- He: undefined,
805
- kn: undefined,
806
- t: 0,
807
- Ce: null,
808
- ye: null,
809
- _: undefined,
810
- Pe: undefined,
811
- ue: undefined,
812
- S: undefined,
813
- Le: false,
814
- i: null,
815
- Hn: undefined,
816
- sn: undefined,
817
- fn: null,
818
- _n: null,
819
- Un: undefined
588
+ return e.m ??= {
589
+ Te: null,
590
+ Ne: null,
591
+ F: undefined,
592
+ de: undefined,
593
+ Se: undefined,
594
+ Je: undefined,
595
+ Le: undefined,
596
+ Qe: null,
597
+ ve: null,
598
+ He: null,
599
+ ke: false,
600
+ ze: NOT_PENDING,
601
+ Xe: -1,
602
+ _e: 0,
603
+ B: NOT_PENDING,
604
+ T: 0
820
605
  };
821
606
  }
822
607
 
608
+ /** A tracked read of a held node (the reader derives from its transaction's
609
+ * world): the reading pass is marked as having read it (born-held decision
610
+ * in `recompute`), and the flush joins the node's transaction — unless the
611
+ * reader is a render effect. A render effect is the frame, not a derivation
612
+ * (rule 3): in that transaction's own flush, or born into it (uninitialized,
613
+ * A29), it reads the staged value and holds nothing of its own; otherwise it
614
+ * reads the committed value instead (`frameRead`). A node born into the
615
+ * future has no committed value: every reader joins it. A first pass is
616
+ * something not ready under a loading boundary that has not shown content:
617
+ * the boundary collects it and shows its fallback, and the pass is the
618
+ * boundary's, not the tick's (A29's boundary exemption, #3540). */ function joinPass(e, t) {
619
+ e.R |= REACTIVE_JOINED;
620
+ // A new memo's first pass is the hold's work (born held) but not the
621
+ // tick's: a render effect that reads it decides — under a boundary that
622
+ // catches it, the boundary's; otherwise the tick joins the hold (#3540:
623
+ // the reader's position, not where the memo was created). Its catch, if
624
+ // any, is at its staging.
625
+ if (t.S & STATUS_UNINITIALIZED || e.u !== EFFECT_RENDER) joinPassTx(txOf(t), !e.u && e.S & STATUS_UNINITIALIZED || GlobalQueue.fe?.(e));
626
+ }
627
+
628
+ /** A15's stale reader (shared-hole and reveal corollaries): a render effect
629
+ * reading a node held by a transaction that is not the flush's. It is served
630
+ * the committed value — a pending one's too — publishes mainline, and is
631
+ * re-derived after that transaction's landing (`_reruns`,
632
+ * REACTIVE_FRAME_READ) so the frame catches up with it once it is the
633
+ * committed world. Not a frame born into that transaction (held and never
634
+ * committed — created by one of its held passes, A29; `bornInto`): that
635
+ * one reads its transaction's values and keeps staging. Not of a flight
636
+ * whose inputs are already visible (CONFIG_INPUTS_PUBLISHED, #3305): its
637
+ * committed value would tear against them, so the reveal observes the
638
+ * flight instead. Once per pass.
639
+ *
640
+ * Lane work is a stale reader too (maintainer, 2026-10-01: a lane sees the
641
+ * screen plus its own guesses). A held write is held because showing it
642
+ * would tear against the rest of its frame; a lane revealing a derivation
643
+ * of it would show it anyway, beside inputs still committed elsewhere on
644
+ * the page. The guess is the one value the user opted into predicting. So
645
+ * a lane pass reading a transaction's held node — its parent's or any
646
+ * other's — derives from the committed value and is re-derived at that
647
+ * landing. The case read-through was built for — a corrected guess whose
648
+ * downstream async must refetch with the truth at once, while another of
649
+ * the parent's flights is still up — needs none of this: the correction
650
+ * dissolves the lane, and the refetching pass is the parent's own, reading
651
+ * the truth as a member (`joinPass`). */ function frameRead(e, t) {
652
+ // Frame readers: a render effect, a verdict reader (CONFIG_VERDICT — it
653
+ // reads the screen like one: `[isPending(x), x()]` never pairs the fresh
654
+ // value with pending, A10, because the reader never sees the fresh value),
655
+ // and lane work (a lane sees the screen plus its own guesses). Not a
656
+ // mount's memo (2026-10-02, considered and reverted): a memo is not a
657
+ // leaf — the transaction's own later passes read it — so a mainline mount's
658
+ // derivations carry the future (A29, born held); only its direct bindings
659
+ // read the screen. Lane work with no committed value yet reads a flight
660
+ // as a mount's memo does: it enters, and the boundary it mounts catches
661
+ // the pending (#3540).
662
+ const n = e.i & CONFIG_VERDICT;
663
+ if ((passLane === null || e.S & STATUS_UNINITIALIZED && t.S & STATUS_PENDING) && !n && (e.u !== EFFECT_RENDER || t.i & CONFIG_INPUTS_PUBLISHED)) return false;
664
+ const E = txOf(t);
665
+ // Lane work and verdict readers are never the transaction's pass, whichever
666
+ // flush runs it: a node T holds re-enters T when it re-runs (`recompute`'s
667
+ // head), and the pass may then read a guess and become the lane's.
668
+ if (E === flushTransaction && passLane === null && !n || bornInto(e, E)) return false;
669
+ staleReader(e, E);
670
+ return true;
671
+ }
672
+
673
+ /** A node born into transaction `t`: held by it and never committed (A29,
674
+ * ruling A) — uninitialized (born held), or a member of the frame one of its
675
+ * held passes built (CONFIG_HELD_FRAME). Its branch exists only in `t`: it
676
+ * has no screen to read, so it reads `t`'s values, whichever flush runs it,
677
+ * and keeps staging (#4010). */ function bornInto(e, t) {
678
+ return (e.S & STATUS_UNINITIALIZED || e.i & CONFIG_HELD_FRAME) && e.i & CONFIG_HELD && txOf(e) === t;
679
+ }
680
+
681
+ /** The computed whose pass a creation under `owner` belongs to (a root's is
682
+ * its nearest computed ancestor's), or null outside every pass. */ function creatorPass(e) {
683
+ return e !== null && e.$e ? e.et : e;
684
+ }
685
+
686
+ /** L2 — park the previous pass's frame for this node's commit: the children
687
+ * (already allocated — nothing new) and the `_disposal` list move to the
688
+ * extension, keep reacting from the heap, and die when the commit publishes
689
+ * the frame that replaces them (`commitPendingNode`) or when the owner does. */ function parkChildren(e) {
690
+ markDisposal(e);
691
+ const t = ext(e);
692
+ t.ve = e.Re;
693
+ t.Qe = e.Ve;
694
+ e.Re = null;
695
+ e.Ve = null;
696
+ e.Be = 0;
697
+ }
698
+
823
699
  /**
824
700
  * Build an Effect node with all effect-specific fields baked into a single object literal,
825
701
  * so V8 sees the full hidden class shape at construction time. Effects always run in lazy
826
702
  * mode (recompute is called explicitly by `effect()`), so we hardcode the lazy bits and skip
827
703
  * the auto-dispose CONFIG bit (effect() previously cleared it post-construction).
828
- */ function createEffectNode(e, n, t, i, u) {
829
- const l = u?.transparent ?? false;
704
+ */ function createEffectNode(e, t, n, E, u) {
705
+ const s = u?.transparent ?? false;
830
706
  // Prod and observe boilerplates — see computed() for why the observe tier
831
707
  // gets its `_name` as a literal slot rather than a write after the fact.
832
708
  // The default label is the node kind (tracked effects relabel their computed
833
709
  // in trackedEffect); the wrappers in signals.ts no longer spread a name into
834
710
  // the options to get it.
835
- const r = {
836
- id: inheritId(u, l, context),
837
- C: (l ? CONFIG_TRANSPARENT : 0) | (u?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (u?.sync ? CONFIG_SYNC : 0) | (u?.yn ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
838
- xe: false,
839
- qe: null,
840
- T: context?.T ?? globalQueue,
841
- Je: context?.Je ?? defaultContext,
842
- En: 0,
843
- he: e,
844
- ce: undefined,
845
- rn: 0,
846
- Pn: undefined,
847
- Fn: null,
848
- Ie: null,
849
- Nn: null,
850
- Ye: 0,
851
- u: null,
852
- mn: null,
853
- _parent: context,
854
- un: null,
855
- gn: null,
856
- tn: null,
857
- oe: REACTIVE_LAZY,
858
- h: STATUS_UNINITIALIZED,
859
- be: clock,
860
- _e: NOT_PENDING,
711
+ const i = {
712
+ id: inheritId(u, s, context),
713
+ i: (s ? CONFIG_TRANSPARENT : 0) | (u?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (u?.sync ? CONFIG_SYNC : 0) | (u?.Me ?? 0) | (snapshotCaptureActive && ownerInSnapshotScope(context) ? CONFIG_IN_SNAPSHOT_SCOPE : 0),
714
+ ye: false,
715
+ Re: null,
716
+ D: context?.D ?? defaultContext,
717
+ Be: 0,
718
+ ee: e,
719
+ Ae: undefined,
720
+ me: 0,
721
+ je: undefined,
861
722
  we: null,
862
- vn: -1,
863
- ve: false,
864
- Xe: false,
865
- xn: undefined,
866
- Qn: n,
867
- Mn: t,
868
- wn: undefined,
869
- je: i,
870
- pn: null,
871
- o: null
723
+ p: null,
724
+ G: null,
725
+ pe: 0,
726
+ t: null,
727
+ Ye: null,
728
+ _parent: context,
729
+ ge: null,
730
+ Ke: null,
731
+ Ve: null,
732
+ R: REACTIVE_LAZY,
733
+ S: STATUS_UNINITIALIZED,
734
+ Ie: clock,
735
+ Oe: NOT_PENDING,
736
+ qe: -1,
737
+ De: false,
738
+ Ge: false,
739
+ be: undefined,
740
+ Et: t,
741
+ ut: n,
742
+ st: undefined,
743
+ u: E,
744
+ m: null
872
745
  };
873
746
  // Effects dispatch status through the SHARED notifier (statusNotifierOf,
874
747
  // keyed off _type) — storing it per node forced a full NodeExtension
875
748
  // allocation on EVERY effect at creation (an alloc + 19 field stores,
876
749
  // +23% effect creation, caught by the creation benches). Only genuinely
877
750
  // per-node channels (boundaries) live on _x.
878
- if (u?.unobserved) ext(r).Hn = u.unobserved;
879
- setupComputedNode(r, lazyOptions);
880
- return r;
751
+ if (u?.unobserved) ext(i).Je = u.unobserved;
752
+ setupComputedNode(i, lazyOptions);
753
+ return i;
881
754
  }
882
755
 
883
756
  /**
884
757
  * The shared status notifier for effect nodes, installed once by effect.ts
885
758
  * at module evaluation (`this`-dispatched — one function serves every
886
- * effect, so nodes never store it). Boundary computeds keep their own
887
- * per-node channel on `_x._notifyStatus`, which takes precedence.
759
+ * effect, so nodes never store it). CARVE 4: the boundaries' per-node
760
+ * channel (`_x._notifyStatus`) went with them; effects are the only
761
+ * display consumers.
888
762
  */ let effectStatusNotify = null;
889
763
 
890
764
  function setEffectStatusNotify(e) {
891
765
  effectStatusNotify = e;
892
766
  }
893
767
 
894
- /** Resolve a node's status notifier: an own `_x` channel (boundaries) wins;
895
- * effect nodes (`_type` — EFFECT_PURE is 0, and only effect literals carry
896
- * the field) fall back to the shared notifier. Presence doubles as the
897
- * "display consumer" membership test in the status walks, exactly as the
898
- * per-node field did when every effect carried one. */ function statusNotifierOf(e) {
899
- const n = e.o?.S;
900
- if (n !== undefined) return n;
901
- return e.je ? effectStatusNotify ?? undefined : undefined;
768
+ /** Resolve a node's status notifier: effect nodes (`_type` — EFFECT_PURE is
769
+ * 0, and only effect literals carry the field) get the shared notifier.
770
+ * Presence doubles as the "display consumer" membership test in the status
771
+ * walks, exactly as the per-node field did when every effect carried one. */ function statusNotifierOf(e) {
772
+ return e.u ? effectStatusNotify ?? undefined : undefined;
902
773
  }
903
774
 
904
775
  const lazyOptions = {
905
776
  lazy: true
906
777
  };
907
778
 
908
- function setupComputedNode(e, n) {
909
- e.Fn = e;
910
- const t = context?.Wn ? context.qn : context;
779
+ function setupComputedNode(e, t) {
780
+ e.we = e;
781
+ const n = context?.$e ? context.et : context;
911
782
  if (context) linkChild(context, e);
912
- if (t) e.rn = t.rn + 1;
913
- if (GlobalQueue.Zn !== null) GlobalQueue.Zn(e);
914
- !n?.lazy && recompute(e, true);
915
- if (snapshotCaptureActive && !n?.lazy) {
916
- if (!(e.h & STATUS_PENDING) && !(e.C & CONFIG_NO_SNAPSHOT)) {
917
- ext(e).sn = e.ce === undefined ? NO_SNAPSHOT : e.ce;
918
- e.C |= CONFIG_HAS_SNAPSHOT;
783
+ if (n) e.me = n.me + 1;
784
+ GlobalQueue.it?.(e);
785
+ !t?.lazy && recompute(e, true);
786
+ if (snapshotCaptureActive && !t?.lazy) {
787
+ if (!(e.S & STATUS_PENDING) && !(e.i & CONFIG_NO_SNAPSHOT)) {
788
+ ext(e).Le = e.Ae === undefined ? NO_SNAPSHOT : e.Ae;
789
+ e.i |= CONFIG_HAS_SNAPSHOT;
919
790
  snapshotSources.add(e);
920
791
  }
921
792
  }
922
793
  }
923
794
 
924
- function signal(e, n, t = null) {
795
+ function signal(e, t) {
925
796
  // Prod and observe boilerplates — see computed(). The observe literal adds
926
797
  // `_name` and `_owner` (the creating owner, stamped by registerGraph for
927
798
  // createSignal nodes so ownerPath can locate signal subjects; null here,
928
799
  // and staying null on internal signals — one shape either way).
929
- const i = {
930
- xe: n?.equals ?? isEqual,
931
- C: (n?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (n?.H ? CONFIG_NO_SNAPSHOT : 0),
932
- ce: e,
933
- u: null,
934
- mn: null,
935
- be: clock,
936
- De: t,
937
- Ue: t?.o?.i || null,
938
- Yn: null,
939
- _e: NOT_PENDING,
800
+ const n = {
801
+ ye: t?.equals ?? isEqual,
802
+ i: (t?.ownedWrite ? CONFIG_OWNED_WRITE : 0) | (t?.ae ? CONFIG_NO_SNAPSHOT : 0),
803
+ Ae: e,
804
+ t: null,
805
+ Ye: null,
806
+ Ie: clock,
807
+ Oe: NOT_PENDING,
940
808
  // Signal-literal diet (§12e): NO _time/_fn/_statusFlags slots. Stores
941
809
  // materialize one signal per touched leaf, so signal bytes are store
942
810
  // bytes. _time is write-only on signals (every read site is computed-
943
811
  // typed error-retry gating); _fn/_statusFlags read falsy-identically as
944
812
  // missing properties on the shared paths (undefined masks to 0).
945
- we: null,
946
- vn: -1,
947
- o: null
813
+ qe: -1,
814
+ m: null
948
815
  };
949
- if (n?.unobserved) ext(i).Hn = n.unobserved;
950
- if (t) linkFirewallChild(t, i);
951
- if (snapshotCaptureActive && !(i.C & CONFIG_NO_SNAPSHOT) && !((t?.h ?? 0) & STATUS_PENDING)) {
952
- ext(i).sn = e === undefined ? NO_SNAPSHOT : e;
953
- i.C |= CONFIG_HAS_SNAPSHOT;
954
- snapshotSources.add(i);
816
+ if (t?.unobserved) ext(n).Je = t.unobserved;
817
+ if (snapshotCaptureActive && !(n.i & CONFIG_NO_SNAPSHOT)) {
818
+ ext(n).Le = e === undefined ? NO_SNAPSHOT : e;
819
+ n.i |= CONFIG_HAS_SNAPSHOT;
820
+ snapshotSources.add(n);
955
821
  }
956
- return i;
957
- }
958
-
959
- // ---------------------------------------------------------------------------
960
- // SLOT SIGNALS (store leaves) — the create-floor diet. Store mounts
961
- // materialize one signal per touched leaf (~13 × rows on dbmon), so per-node
962
- // allocations are mount bytes: the generic path costs an options object, an
963
- // equals closure, an unobserved closure, a NodeExtension to hold it, and
964
- // three post-construction expandos (acc/px/pxv → hidden-class transitions).
965
- // slotSignal bakes everything into ONE literal: `_host`/`_key` backrefs
966
- // replace the closures (equals is a method call — `this` is the node; the
967
- // unobserved sweep dispatches CONFIG_SLOT_NODE to one shared hook), and the
968
- // store's wrap-cache fields are pre-shaped.
969
- /** The shared slot-node unobserved handler — a live binding read directly by
970
- * the sweep sites (no wrapper frame, no null check: a CONFIG_SLOT_NODE node
971
- * existing implies the store module loaded and registered the hook). */ let slotUnobservedHook;
972
-
973
- /** Install the shared slot-node unobserved handler (store module, once). */ function setSlotUnobserved(e) {
974
- slotUnobservedHook = e;
975
- }
976
-
977
- /** Push a new node onto its firewall's child chain (the literal already
978
- * points `_nextChild` at the old head). Doubly linked so a released leaf
979
- * unlinks in O(1) — the chain is walked per mark of the projection and
980
- * would otherwise grow by one node per leaf ever read (#3351). */ function linkFirewallChild(e, n) {
981
- const t = n.Ue;
982
- if (t !== null) t.Yn = n;
983
- ext(e).i = n;
984
- e.C |= CONFIG_FW_CHILDREN;
822
+ return n;
985
823
  }
986
824
 
987
- /** Release a firewall child the store no longer addresses (unobserved sweep
988
- * dropped it from its target's cache): unlink it from the chain so the
989
- * projection stops retaining it and its last value. The node keeps its own
990
- * `_nextChild` so a walk that is mid-chain on it still terminates. It also
991
- * leaves `_companionChildren` (#3503): the companions themselves are
992
- * permanent on the node, but the node is unreachable through the store, so
993
- * the set would only retain it and its last value. */ function unlinkFirewallChild(e) {
994
- const n = e;
995
- const t = n.De;
996
- if (!t) return;
997
- const i = n.Yn;
998
- const u = n.Ue;
999
- if (i !== null) i.Ue = u; else if (t.o.i === n) t.o.i = u;
1000
- if (u !== null) u.Yn = i;
1001
- n.Yn = null;
1002
- // #3503: the companion set is the last projection-side reference to a
1003
- // released leaf; leaving it there kept the leaf and its last value alive
1004
- // for the projection's lifetime after every latest()/isPending() reader
1005
- // was disposed.
1006
- t.o.Un?.delete(n);
825
+ function isEqual(e, t) {
826
+ return e === t;
1007
827
  }
1008
828
 
1009
- function slotSignal(e, n, t, i, u, l = null) {
1010
- // Prod and observe boilerplates — see computed(). The store relabels the
1011
- // observe slot (`store.<key>`) when the attribution engine is installed.
1012
- const r = {
1013
- xe: n,
1014
- C: CONFIG_OWNED_WRITE | CONFIG_SLOT_NODE,
1015
- ce: e,
1016
- u: null,
1017
- mn: null,
1018
- be: clock,
1019
- De: l,
1020
- Ue: l?.o?.i || null,
1021
- Yn: null,
1022
- _e: NOT_PENDING,
1023
- we: null,
1024
- vn: -1,
1025
- o: null,
1026
- // Slot backrefs: what the equals/unobserved closures used to capture.
1027
- Bn: t,
1028
- Kn: i,
1029
- // Store read-path caches, pre-shaped (were post-construction expandos).
829
+ /** A store slot node (CONFIG_SLOT_NODE): the whole node in ONE literal — the
830
+ * target and key as back-refs in place of closures (`equals` is a method
831
+ * call, `this` the node; the unobserved sweep dispatches to one shared hook,
832
+ * graph.ts), the store's wrap cache (`px`/`pxv`: the proxy last served for
833
+ * this key and the raw it wrapped — one pointer compare replaces a WeakMap
834
+ * lookup per read) and the accessor verdict (`acc`) pre-shaped. No options
835
+ * object, no NodeExtension, no post-construction expandos. `ownedWrite` is
836
+ * baked in: the setter carries the owned-scope guard; node-level writes are
837
+ * the store's notification machinery. The observe literal adds the label
838
+ * slot (the store relabels it `store.<key>` when the engine is installed). */ function slotSignal(e, t, n, E, u) {
839
+ const s = {
840
+ ye: t,
841
+ i: CONFIG_OWNED_WRITE | CONFIG_SLOT_NODE,
842
+ Ae: e,
843
+ t: null,
844
+ Ye: null,
845
+ Ie: clock,
846
+ Oe: NOT_PENDING,
847
+ qe: -1,
848
+ m: null,
849
+ lt: n,
850
+ ot: E,
1030
851
  acc: u,
1031
852
  px: undefined,
1032
853
  pxv: undefined
1033
854
  };
1034
- if (l) linkFirewallChild(l, r);
1035
- if (snapshotCaptureActive && !((l?.h ?? 0) & STATUS_PENDING)) {
1036
- ext(r).sn = e === undefined ? NO_SNAPSHOT : e;
1037
- r.C |= CONFIG_HAS_SNAPSHOT;
1038
- snapshotSources.add(r);
1039
- }
1040
- return r;
1041
- }
1042
-
1043
- function optimisticSignal(e, n) {
1044
- const t = signal(e, n);
1045
- ext(t).Fe = NOT_PENDING;
1046
- t.C |= CONFIG_OPTIMISTIC;
1047
- return t;
855
+ return s;
1048
856
  }
1049
857
 
1050
- function optimisticComputed(e, n) {
1051
- const t = computed(e, n);
1052
- ext(t).Fe = NOT_PENDING;
1053
- t.C |= CONFIG_OPTIMISTIC;
1054
- return t;
1055
- }
858
+ /**
859
+ * When set to a component name string, any reactive read that is not inside a nested tracking
860
+ * scope will log a dev-mode warning. Managed automatically by `untrack(fn, strictReadLabel)`.
861
+ */
862
+ /** The verdict windows' read (verdict.ts), installed while a `latest` or
863
+ * `isPending` window is open and null otherwise: `read` dispatches to it
864
+ * then, and a program that never imports them never has one. */ let verdict = null;
1056
865
 
1057
- function isEqual(e, n) {
1058
- return e === n;
866
+ function setVerdict(e) {
867
+ verdict = e;
1059
868
  }
1060
869
 
1061
870
  /**
@@ -1079,15 +888,15 @@ function isEqual(e, n) {
1079
888
  * }
1080
889
  * );
1081
890
  * ```
1082
- */ function untrack(e, n) {
1083
- if (GlobalQueue.jn === null && !tracking && true) return e();
1084
- const t = tracking;
891
+ */ function untrack(e, t) {
892
+ if (GlobalQueue._t === null && !tracking && true) return e();
893
+ const n = tracking;
1085
894
  tracking = false;
1086
895
  try {
1087
- if (GlobalQueue.jn !== null) return GlobalQueue.jn(e);
896
+ if (GlobalQueue._t) return GlobalQueue._t(e);
1088
897
  return e();
1089
898
  } finally {
1090
- tracking = t;
899
+ tracking = n;
1091
900
  }
1092
901
  }
1093
902
 
@@ -1103,685 +912,365 @@ function isEqual(e, n) {
1103
912
  * born-held tree would otherwise pull the mounting pass into the hold).
1104
913
  */ let spectating = false;
1105
914
 
1106
- /**
1107
- * Evaluates `fn` untracked, recording nothing on the current `context`: no
1108
- * untracked-pending re-run link, no transaction entry. For bookkeeping reads
1109
- * made on behalf of no node — see `spectating`.
1110
- */ function spectate(e) {
1111
- const n = spectating;
1112
- spectating = true;
1113
- try {
1114
- return untrack(e);
1115
- } finally {
1116
- spectating = n;
1117
- }
1118
- }
1119
-
1120
915
  /**
1121
916
  * Bring a computed to a readable state: lazy/disposed nodes are (re)computed;
1122
- * an isPending() probe (`refresh`) additionally pulls the node fully up to
1123
- * date so its status flags reflect the current graph.
1124
- */ function prepareComputed(e, n) {
1125
- if (e.oe & REACTIVE_LAZY) {
1126
- e.oe &= ~REACTIVE_LAZY;
917
+ * `refresh` additionally pulls the node fully up to date so its status flags
918
+ * reflect the current graph.
919
+ */ function prepareComputed(e, t) {
920
+ if (e.R & REACTIVE_LAZY) {
921
+ e.R &= ~REACTIVE_LAZY;
1127
922
  recompute(e, true);
1128
- } else if (e.oe & REACTIVE_DISPOSED) {
923
+ } else if (e.R & REACTIVE_DISPOSED) {
1129
924
  // Two disposal lifecycles share the flag (#3024). Observation-lifecycle
1130
925
  // nodes (CONFIG_AUTO_DISPOSE) are dormant — torn down by unobserved()
1131
926
  // when the last subscriber left — and reads reawaken them; that is the
1132
927
  // pay-for-use contract. Owner-lifecycle nodes are dead: recomputing would
1133
928
  // re-run user code in a torn-down tree (and discard manual writes on
1134
929
  // derived-writable signals), so reads return the last committed value.
1135
- if (e.C & CONFIG_AUTO_DISPOSE) {
1136
- const n = e._parent;
1137
- if (n !== null) {
930
+ if (e.i & CONFIG_AUTO_DISPOSE) {
931
+ const t = e._parent;
932
+ if (t !== null) {
1138
933
  // A dormant node was off the chain when its owner died, so the strip
1139
934
  // in disposeChildren missed it: freeze here instead (#3024).
1140
- if (n.oe & REACTIVE_DISPOSED) {
1141
- e.C &= ~CONFIG_AUTO_DISPOSE;
935
+ if (t.R & REACTIVE_DISPOSED) {
936
+ e.i &= ~CONFIG_AUTO_DISPOSE;
1142
937
  return;
1143
938
  }
1144
- // A zombie never left its chain: settleAutodispose releases without
1145
- // the !ZOMBIE check its siblings have, so this guard is load-bearing.
1146
- if (!(e.oe & REACTIVE_ZOMBIE)) linkChild(n, e);
939
+ // A zombie never left its chain (the parked frame is drained whole,
940
+ // and its members skip the splice): relinking would put it on the
941
+ // live chain too.
942
+ if (!(e.R & REACTIVE_ZOMBIE)) linkChild(t, e);
1147
943
  }
1148
944
  recompute(e, true);
1149
945
  }
1150
- } else if (n) {
1151
- updateIfNecessary(e);
1152
- }
946
+ } else ;
1153
947
  }
1154
948
 
949
+ // CARVE 3: until()'s authoritative-view wakeup (notifyAuthoritativeObservers /
950
+ // installAuthoritativeRead), the stale-of-foreign clause (heldFromStale,
951
+ // recordStaleReplay, ownsHold) and the transaction entry on a staged read
952
+ // (enterStagedRead, stagedEntry / born held, underFreshLoadingBoundary) went
953
+ // with the transactions.
1155
954
  /**
1156
- * Sentinel returned by readNodeFast when the plain-signal fast path does not
1157
- * apply and the caller must fall back to the full read().
1158
- */ const READ_SLOW = Symbol("read-slow");
1159
-
1160
- /**
1161
- * read()'s plain-signal fast path as a standalone entry for hot callers
1162
- * (store traps). Safe to substitute for read() only because the bail
1163
- * conditions mirror read()'s prelude and fast-path guard exactly: the
1164
- * latestRead and pendingCheck windows run side-effectful hooks before the
1165
- * fast path, `_fn` nodes need prepareComputed, and firewall / override /
1166
- * snapshot / transition / lane / dev-strictRead state all take the full
1167
- * resolution. Anything slow returns READ_SLOW; the caller then calls read().
1168
- */
1169
- /**
1170
- * Wake only authoritative-view readers (until() predicates) subscribed to `el`.
1171
- * The A17-silent ack paths — an authoritative arrival equal to the active
1172
- * override — use this so the predicate re-evaluates without re-firing
1173
- * ordinary subscribers whose visible (override) value did not change.
1174
- * Pay-for-use: reached through GlobalQueue._notifyAuthoritativeObservers,
1175
- * installed at first until() call — apps that never use until() shake it.
1176
- */ function notifyAuthoritativeObservers(e) {
1177
- for (let n = e.u; n !== null; n = n.Ae) {
1178
- const e = n.ge;
1179
- if (!(e.C & CONFIG_AUTHORITATIVE_READ)) continue;
1180
- // Missed-wake latch (#3037), same contract as insertSubs: the reader may
1181
- // itself have pulled this recompute (updateIfNecessary from its own
1182
- // read), and the heap refuses RECOMPUTING nodes — latch so recompute's
1183
- // tail reschedules it with the staged value visible.
1184
- if (e.oe & REACTIVE_RECOMPUTING_DEPS && n.Be === e.Ye && n !== e.Nn) e.oe |= REACTIVE_MISSED_WAKE;
1185
- enqueueSub(e);
1186
- }
1187
- schedule();
1188
- }
1189
-
1190
- /** Installs the authoritative-reader wakeup hook. Idempotent; called by every
1191
- * creator of a CONFIG_AUTHORITATIVE_READ computation — until() and refresh() —
1192
- * before its first read (same late-binding contract as the optimistic engine;
1193
- * the gating bit is only ever set by such a read, so the `!` call sites are
1194
- * safe once every setter installs, #3303). */ function installAuthoritativeRead() {
1195
- if (GlobalQueue.zn === null) GlobalQueue.zn = notifyAuthoritativeObservers;
1196
- }
1197
-
1198
- /**
1199
- * Stale-reader term of the value selections below: a render effect reading a
1200
- * node some OTHER live transaction has staged sees the committed value. The
1201
- * commit is silent — the staging walk was the notification — so a reader
1202
- * that linked AFTER that walk (an effect created during the hold, a store
1203
- * key first read under it) would show the old value past the reveal: record
1204
- * it for the transaction's commit replay (the `_gatedSubs` contract lanes
1205
- * already use). An effect the transaction itself computed re-derives at its
1206
- * commit on its own (parked run, or the contested re-derive, #3322) and is
1207
- * not recorded — replaying it too would publish the frame twice.
1208
- *
1209
- * Flight twin (the pending-branch carve-out): the reader is served the
1210
- * node's committed, pre-flight value and now observes that flight — A15:
1211
- * async work observed by a reader settles as one unit with the writes that
1212
- * asked it — so it joins the transaction's reporters for the node. The
1213
- * reporter the transaction recorded when the flight started may be gone (a
1214
- * keyed remount disposed it, #3374); a completion check that found no live
1215
- * reporter committed the writes ahead of the answer, tearing the new
1216
- * reader's frame (`Count: 1` beside `Details: 0`). Joins an entry the
1217
- * transaction already holds; a flight nobody had observed yet has none (the
1218
- * reader is its first observer — a conditional that just revealed it, #3458)
1219
- * and is notified up the reader's own queue chain under that transaction,
1220
- * the one sanctioned registration site (INV-3): a collecting boundary above
1221
- * the reader consumes it as it would any pending, an unboundaried reader
1222
- * opens the entry — and the transaction, judged complete on its other
1223
- * flights, revealed the inputs beside the reader's pre-flight value
1224
- * otherwise (`Count: 1 | A: 1` beside `B: 0`). A staged signal or a settled
1225
- * node registers nothing. Every reporter dies with its reader
1226
- * (reporterBlocksSource: the read linked it as a dep). The node's own entry
1227
- * is the only one that can matter: a chain's intermediate memo is re-pulled
1228
- * by the read (updateIfNecessary's retry) and enters the transaction, so the
1229
- * reader holds through the normal path; a node with its own flight that is
1230
- * also pending on an upstream re-ask blocks through that flight until it
1231
- * lands, and its landing re-runs the reader into the normal path.
1232
- */
1233
- /** The replay half of the stale-of-foreign clause (A15 / A26): a stale reader
1234
- * served the committed value because `txn` holds what it read re-runs at
1235
- * txn's commit, when the value it was denied becomes the frame — unless its
1236
- * own last value already came from that transaction. One registration for
1237
- * the node path (heldFromStale) and the store's backing paths, which have
1238
- * no node to carry the hold (heldFromReader, the adoption hold view). */ function recordStaleReplay(e, n) {
1239
- const t = n.pn;
1240
- if (t == null || currentTransition(t) !== e) e.In.add(n);
1241
- }
1242
-
1243
- /**
1244
- * The ownership relation (DESIGN-CONSOLIDATION §6, ruled 2026-09-17): is
1245
- * `hold` part of the running pass's world? A plain reader's world is the
1246
- * transaction it runs under, through merges. A lane reader's world is its
1247
- * lane AND the transition that owns the lane — the one asymmetry between a
1248
- * lane and a separate transaction (a lane sees what lands from its parent as
1249
- * its own; a separate transaction would wait for the parent to settle) —
1250
- * see `ownsLane` in lanes.ts, built on this. One relation for the
1251
- * stale-of-foreign clause (heldFromStale), the lane arm (readsHeldCommitted)
1252
- * and the store's backing holds (foreignHold); `serve` has no lane arm of
1253
- * its own, the lane's extra visibility lives here.
1254
- */ function ownsHold(e) {
1255
- return activeTransition !== null && currentTransition(e) === currentTransition(activeTransition);
1256
- }
1257
-
1258
- function heldFromStale(e, n) {
1259
- const t = e.we;
1260
- if (t === null || ownsHold(t)) return false;
1261
- const i = currentTransition(t);
1262
- recordStaleReplay(i, n);
1263
- const u = i.le.get(e);
1264
- if (u) u.add(n); else if (e.h & STATUS_PENDING) runInTransition(i, () => n.T.notify(n, STATUS_PENDING, STATUS_PENDING, e.o._));
1265
- return true;
1266
- }
1267
-
1268
- /**
1269
- * A tracked computation about to be served a live transaction's staged value
1270
- * derives from that transaction's world, so it enters the transaction and
1271
- * this pass's result is held with it (A29, #3408). The write side (`setSignal`)
1272
- * and a stamped node's recompute already enter; a reader that only now
1273
- * starts reading the held node — a conditional memo whose branch flipped —
1274
- * was the gap: it published a value derived from the staged world into the
1275
- * mainline frame. Not for a probe (`isPending(() => x())` observes, it does
1276
- * not derive) and a no-op for the ambient batch (`_transition` null) or the
1277
- * transaction already active.
1278
- */
1279
- /** The transaction a pass running OUTSIDE a flush was served a staged value
1280
- * from (A29, creation-time form). Inside a flush the pass enters through
1281
- * initTransition; outside one — a memo or effect created from mainline code
1282
- * while a hold is live — entering would leave `activeTransition` and the
1283
- * batch pointed at the transaction for the rest of the synchronous block,
1284
- * so an unrelated write made after the mount was held with someone else's
1285
- * action. The entry is the pass's alone: recompute stages the node into the
1286
- * transaction (born held) and mainline is never touched.
1287
- *
1288
- * A pass under a FRESH loading boundary takes this path inside a flush too
1289
- * (#3540). Born held is right for a plain memo or effect: published, its
1290
- * value would tear the frame. A boundary that has not revealed yet is the
1291
- * exception by definition — its job is to catch what is not ready under it
1292
- * rather than let it hold: the pass is staged into the transaction as
1293
- * above, and the boundary is told (recompute's born-held arm notifies it
1294
- * with a `NotReadyError` sourced at the node) so it shows its fallback now
1295
- * and reveals the staged result at the commit.
1296
- * Entering instead adopted the whole flush into the hold — a `Show` that
1297
- * flipped mainline to mount a `Loading` over a held value waited for the
1298
- * hold with it. A boundary that already shows content keeps the entering
1299
- * path: it has content to keep, and holds like any reader. */ let stagedEntry = null;
1300
-
1301
- /** Is `el` routed to a loading boundary that still shows its fallback — the
1302
- * nearest pending-collecting queue up its chain is uninitialized? (Measured
1303
- * 2026-09-18: relocating the walk behind a `GlobalQueue` slot installed by
1304
- * boundaries.ts saved ~8 B on the core floor and cost boundary-using apps
1305
- * 50–70 B — the indirection's tokens outweigh the walk. Kept inline.) */ function underFreshLoadingBoundary(e) {
1306
- for (let n = e.T; n !== null; n = n._parent) if (n.te & STATUS_PENDING) return !n.q;
1307
- return false;
1308
- }
1309
-
1310
- function enterStagedRead(e, n = e.we) {
1311
- if (!n || n === activeTransition || pendingCheckActive) return;
1312
- // A companion (the latest() shadow, the isPending() verdict signal) is the
1313
- // engine's mirror of the flushed world — reading it, or being it, is an
1314
- // observation, not a derivation from the hold: latest(x) never enters x's
1315
- // transaction, and the shadow's own pass never enters either (it would
1316
- // flip activeTransition under the reader that pulled it). (`el` is null for
1317
- // a store backing served under a hold — no node, the transaction is the
1318
- // fold's.)
1319
- if (e?.o?.kn || context?.o?.kn) return;
1320
- // A bookkeeping read (`spectate`) is nobody's derivation: it compares or
1321
- // probes, and enters nothing — the boundary priming read of a born-held
1322
- // tree must not enter the creator's pass into the hold (#3540).
1323
- if (spectating) return;
1324
- // Verdict machinery (GlobalQueue._verdictPull: companion creation and the
1325
- // latest()/isPending() pulls — the latest() shadow is created before it is
1326
- // marked optimistic, so the bit alone cannot tell) and optimistic nodes
1327
- // (own lane posture, A31) read staged truth by design; they keep the
1328
- // entering path.
1329
- // (`context` is non-null here: every caller selected a value for a reader.)
1330
- const t = context;
1331
- const i = activeTransition === null && !globalQueue.Jn;
1332
- // Verdict pulls are observations, not derivations: a latest() /
1333
- // isPending() call from mainline must never enter a transaction (it
1334
- // would capture the rest of the caller's synchronous block).
1335
- if (i && GlobalQueue.Xn) return;
1336
- if (t.oe & REACTIVE_RECOMPUTING_DEPS && !(t.C & CONFIG_OPTIMISTIC) && (stagedEntry === null || stagedEntry === n) && (i || !GlobalQueue.Xn && underFreshLoadingBoundary(t))) {
1337
- stagedEntry = n;
1338
- return;
1339
- }
1340
- globalQueue.initTransition(n);
1341
- }
1342
-
1343
- /**
1344
- * Rule 1 (value selection), the full arm: does this reader see a STAGED
1345
- * node's COMMITTED value? One implementation of the rule the fast paths
1346
- * (readNodeFast, read's fast block) carry as their trivial ternary and that
1347
- * every slow site — read's tail, the store's backing selection, the lane and
1348
- * verdict arms — used to restate by hand (docs/DESIGN-CONSOLIDATION.md, move 3b). In order:
955
+ * Rule 1 (value selection): does this reader see a STAGED node's COMMITTED
956
+ * value? One implementation of the rule the fast paths (read's fast block)
957
+ * carry as their trivial ternary. In order:
1349
958
  * - no reader at all (an untracked read) — the committed frame;
1350
- * - a reader under an optimistic lane the engine says reads committed
1351
- * (laneReadsCommitted: another lane's hold, #3460);
1352
959
  * - nothing staged;
1353
960
  * - a children-forbidden reader (createTrackedEffect / onSettled: the frame,
1354
- * never the graph — A32);
1355
- * - a stale reader (render effect) of a FOREIGN transaction's staged write —
1356
- * committed, no entanglement (heldFromStale registers the replay; a node
1357
- * born held has no committed frame to fall back to, `noCommitted`);
1358
- * - HELD truth (#3164, CONFIG_HELD_TRUTH) read by a LANE pass: staged
1359
- * confirming truth — fold-staged onto an armed family, or entangle-stolen
1360
- * by an awaited until() — is masked from lane passes only, owning
1361
- * transaction or not. A lane applies its frame display-ahead at the park,
1362
- * so a lane pass served the truth would paint the confirmation beside the
1363
- * optimism it confirms (`saving=true` beside the saved row — the #3164
1364
- * tear); it keeps committed and is re-run by the reveal's post-revert
1365
- * wake. Every other deriving reader falls through to A29 below: the truth
1366
- * is a staged value like any other, and the pass that derives from it is
1367
- * held with it — including the retaining transaction's own passes, which
1368
- * a superseded override already hands the truth (#3568: masking them
1369
- * composed the landed `length` with rows still masked to committed).
1370
- * latest() and authoritative readers (until()'s predicate) tunnel
1371
- * through — the tunnel that keeps the hold deadlock-free.
1372
- * False means the reader derives from the staged value and enters its
1373
- * transaction (enterStagedRead, A29).
1374
- */ function readerSeesCommitted(e, n, t, i) {
1375
- return !!(!n || currentOptimisticLane !== null && GlobalQueue.$n(e, t, n) || e._e === NOT_PENDING || n.C & CONFIG_CHILDREN_FORBIDDEN || stale && !i && heldFromStale(e, n) || e.C & CONFIG_HELD_TRUTH && currentOptimisticLane !== null && !latestReadActive && !(n.C & CONFIG_AUTHORITATIVE_READ));
961
+ * never the graph — A32).
962
+ * False means the reader derives from the staged value.
963
+ */ function readerSeesCommitted(e, t) {
964
+ return !!(!t || e.Oe === NOT_PENDING || t.i & CONFIG_CHILDREN_FORBIDDEN);
1376
965
  }
1377
966
 
1378
- /** A28 — set when a node is staged (queuePendingNode) or a held node rewritten
1379
- * (stashHeldRewrite) OUTSIDE a flush; cleared when the next flush begins. The
1380
- * read sites test this one module boolean instead of `globalQueue._running`:
1381
- * inside a flush it is false and the A28 arm costs nothing; outside, only a
1382
- * tick with unflushed writes pays the staged-node check. */ let unflushedStaged = false;
967
+ /** A28 — set when a node is staged (queuePendingNode) OUTSIDE a flush;
968
+ * cleared when the next flush begins. The read sites test this one module
969
+ * boolean instead of `globalQueue._running`: inside a flush it is false and
970
+ * the A28 arm costs nothing; outside, only a tick with unflushed writes pays
971
+ * the staged-node check. */ let unflushedStaged = false;
1383
972
 
1384
973
  function markUnflushedStaged() {
1385
974
  unflushedStaged = true;
1386
975
  }
1387
976
 
1388
- /** A28 — a write becomes visible at flush. Outside a flush, a node holding an
1389
- * AMBIENT staged value (no transaction stamp) was written since the last
1390
- * flush: ambient staging commits at flush end, so nothing else leaves a node
1391
- * in this state; a stamped value is the flushed held world, which A28 says
1392
- * latest() serves. Inside a flush the rule does not apply (A28 (4): promoted
1393
- * within the round). Structural — no marker on the write path. */ function unflushed(e) {
1394
- return unflushedValue(e) !== NOT_PENDING;
977
+ /** A28 — a write becomes visible at flush. Outside a flush, a node holding a
978
+ * staged value was written since the last flush: staging commits at flush
979
+ * end, so nothing else leaves a node in this state. Inside a flush the rule
980
+ * does not apply (A28 (4): promoted within the round). The value an
981
+ * unflushed node serves is its committed value, or NOT_PENDING when nothing
982
+ * is unflushed. Exempt: promoted writes (A28 (4): a write issued inside a
983
+ * recompute is promoted at that recompute's end — boundary and loading
984
+ * machinery, signals declared for in-computation writes). */ function unflushedValue(e) {
985
+ if (globalQueue.X || e.Oe === NOT_PENDING || e.i & CONFIG_PROMOTED) return NOT_PENDING;
986
+ // A held node (L2) carries a staged value between flushes too, but it was
987
+ // flushed — and parked. Not an unflushed write — unless rewritten since:
988
+ // then the flushed staging is what the world sees until the next flush.
989
+ if (e.i & CONFIG_HELD) return e.m.Xe === clock ? e.m.ze : NOT_PENDING;
990
+ return e.Ae;
1395
991
  }
1396
992
 
1397
- /** The value an unflushed node serves — the committed value for an ambient
1398
- * write, the flushed staged value for a rewrite of a held node — or
1399
- * NOT_PENDING when nothing is unflushed. Exempt: owned-write nodes (A28 (4):
1400
- * a write issued inside a recompute is promoted at that recompute's end —
1401
- * boundary and loading machinery, until()'s internals, signals declared for
1402
- * in-computation writes) and engine companions (the isPending() verdict
1403
- * signal, the latest() shadow: the system's own writes, made at the source's
1404
- * write to mirror it, installing eagerly — A28, A8). */ function unflushedValue(e, n = e.ce) {
1405
- if (globalQueue.Jn || e._e === NOT_PENDING || e.C & CONFIG_PROMOTED || e.o?.kn) return NOT_PENDING;
1406
- // Ambient, or adopted by a transaction before any flush carried the staging
1407
- // (CONFIG_ADOPTED_UNFLUSHED): nothing flushed is staged — the committed
1408
- // value answers (the caller's notion of committed: a store node's backing).
1409
- // A held node: unflushed only if rewritten since the last flush (stash).
1410
- if (e.we === null || e.C & CONFIG_ADOPTED_UNFLUSHED) return n;
1411
- return e.o === null ? NOT_PENDING : e.o.bn;
993
+ /** A28: a held node rewritten outside a flush keeps the staging the last
994
+ * flush left (`_flushed`) until the flush that carries the rewrite — the
995
+ * stash is stamped with the clock, which that flush advances. */ function stashFlushed(e) {
996
+ const t = e.m;
997
+ if (t.Xe !== clock) {
998
+ t.ze = e.Oe;
999
+ t.Xe = clock;
1000
+ unflushedStaged = true;
1001
+ }
1412
1002
  }
1413
1003
 
1414
- /** Held nodes rewritten since the last flush (setSignal); the flush clears
1415
- * their stash — from then on latest() answers with the rewrite. */ const unflushedRewrites = [];
1416
-
1417
1004
  /** Nodes written inside a creation-time recompute (CONFIG_PROMOTED). */ const promotedWrites = [];
1418
1005
 
1419
- /** A28 (5): an optimistic write becomes the ACTIVE override at the flush that
1420
- * carries it. `_overrideTime` is stamped with `clock` at the write and `clock`
1421
- * advances after every flush, so "this tick, outside a flush" is unflushed. */ function unflushedOverride(e) {
1422
- // Companions are optimistic signals written by the engine (see unflushed).
1423
- return !globalQueue.Jn && e.o?.hn === clock && !e.o?.kn;
1424
- }
1425
-
1426
- /** Active optimistic override on an armed node (an armed slot idles at
1427
- * NOT_PENDING; undefined = unarmed plain node). The writer's own channels —
1428
- * the draft, `in`/keys inside the setter — compose on this regardless of
1429
- * flush state. */ function hasActiveOverride(e) {
1430
- const n = e.o;
1431
- return n !== null && n.Fe !== undefined && n.Fe !== NOT_PENDING;
1432
- }
1433
-
1434
- /** The override a READER sees: installed, and carried by a flush (A28 (5) —
1435
- * an optimistic write is a write; until its flush no reader sees it). One
1436
- * implementation for read()'s override arm, the verdict channels
1437
- * (latestRead, computePendingState) and the store's selection
1438
- * (docs/DESIGN-CONSOLIDATION.md, move 3b). */ function visibleOverride(e) {
1439
- return hasActiveOverride(e) && !unflushedOverride(e);
1440
- }
1441
-
1442
1006
  /** A derivation served the committed value because of an unflushed write
1443
1007
  * (A28) must run again in the flush that carries it — the late-linker case
1444
1008
  * (#3337's reason to defer the walk): it linked after the write walked. */ function markLateLinker(e) {
1445
1009
  // The pass's own tail re-enqueues on this latch (recompute's finally) —
1446
1010
  // a direct enqueue here would be wiped by the pass's flag reset.
1447
- e.oe |= REACTIVE_MISSED_WAKE;
1011
+ e.R |= REACTIVE_MISSED_WAKE;
1448
1012
  return true;
1449
1013
  }
1450
1014
 
1451
- /** Companion-bearing nodes written outside a flush (setSignal); the flush
1452
- * that carries their writes re-syncs their companions (A28). */ const unflushedCompanions = [];
1453
-
1454
1015
  function resyncUnflushedCompanions() {
1455
1016
  unflushedStaged = false;
1456
- // Length-guarded: the common flush has nothing here, and must allocate nothing.
1457
1017
  // Length-guarded: the common flush has nothing here and allocates nothing.
1458
- if (unflushedRewrites.length !== 0) {
1459
- for (const e of unflushedRewrites) e.o.bn = NOT_PENDING;
1460
- unflushedRewrites.length = 0;
1461
- }
1462
- if (promotedWrites.length !== 0) {
1463
- for (const e of promotedWrites) e.C &= ~CONFIG_PROMOTED;
1018
+ if (promotedWrites.length !== 0) {
1019
+ for (const e of promotedWrites) e.i &= ~CONFIG_PROMOTED;
1464
1020
  promotedWrites.length = 0;
1465
1021
  }
1466
- if (unflushedCompanions.length !== 0) {
1467
- for (const e of unflushedCompanions) GlobalQueue.Ve(e, e._e !== NOT_PENDING ? e._e : e.ce);
1468
- unflushedCompanions.length = 0;
1469
- }
1470
1022
  }
1471
1023
 
1472
- function readNodeFast(e) {
1473
- if (latestReadActive || pendingCheckActive || e.he || e.De || e.o?.Fe !== undefined || e.o?.sn !== undefined || activeTransition !== null || currentOptimisticLane !== null || snapshotCaptureActive ||
1474
- // A28: a staged node read outside a flush may serve its committed value;
1475
- // that arm lives on the slow path so this body stays inlinable.
1476
- unflushedStaged && e._e !== NOT_PENDING || false) return READ_SLOW;
1477
- let n = context;
1478
- if (n?.Wn) n = n.qn;
1479
- if (n && tracking) link(e, n);
1480
- // Children-forbidden readers (createTrackedEffect / onSettled callbacks) get
1481
- // committed visibility: like the effect half of createEffect and event
1482
- // handlers, effect-phase code never observes its own unsettled write — the
1483
- // write lands in the same flush's continuation (#3006).
1484
- // A stale reader (render effect) recomputing with no transaction active is
1485
- // mainline: a write staged by a live transaction (`_transition` stamped —
1486
- // ambient staging never is) stays masked until that transaction's reveal,
1487
- // the same rule the slow path applies (#3322). Without it a zombie
1488
- // recompute, or a commit-time re-derive of a contested effect, published
1489
- // another transaction's uncommitted value.
1490
- return !n || e._e === NOT_PENDING || n.C & CONFIG_CHILDREN_FORBIDDEN || stale && heldFromStale(e, n) ? e.ce : (enterStagedRead(e),
1491
- e._e);
1024
+ /** A tracked read's pull of a computed: bring it current when the flush has
1025
+ * reached its height (or the reader is a fresh-pull waiter), and sit the
1026
+ * reader above it. */ function pullComputed(e, t) {
1027
+ if (e.me >= dirtyQueue.Ue) {
1028
+ markNode(t);
1029
+ markHeap(dirtyQueue);
1030
+ updateIfNecessary(e);
1031
+ }
1032
+ // Fresh-pull readers (awaitable refresh's waiter) recompute a dirty
1033
+ // source inline even when the height gate defers to the flush: the
1034
+ // waiter must park on the re-ask's window (or serve its sync answer),
1035
+ // never read the PRE-re-ask value as settled. Self-guarded: a clean
1036
+ // node no-ops and updateIfNecessary refuses disposed nodes (#2983) —
1037
+ // a dead target serves its last value, which is already quiescent.
1038
+ else if (t.i & CONFIG_FRESH_READ) updateIfNecessary(e);
1039
+ const n = e.me;
1040
+ // parent check is shallow, might need to be recursive
1041
+ if (n >= t.me && e._parent !== t) t.me = n + 1;
1492
1042
  }
1493
1043
 
1494
1044
  function read(e) {
1495
- // Handle latest() mode: read from _latestValueComputed
1496
- // Checked before isPending so that isPending(() => latest(x)) checks
1497
- // the _pendingSignal of _latestValueComputed (async in flight) rather
1498
- // than the original node (which stays "pending" while held in a transition).
1499
- if (latestReadActive) return GlobalQueue.et(e);
1500
- let n = context;
1501
- if (n?.Wn) n = n.qn;
1502
- const t = e;
1503
- const i = e.De;
1504
- const u = i || e;
1505
- // Handle isPending() mode: collect pending state while preserving normal read semantics.
1506
- // Probe mode is suspended while preparing the node so nested reads during a
1507
- // recompute don't collect into the probe.
1508
- if (pendingCheckActive) {
1509
- GlobalQueue.nt(e, n, u, i);
1510
- } else if (typeof t.he === "function") {
1511
- prepareComputed(e, false);
1512
- }
1513
- if (!t.he && u === e && e.o?.Fe === undefined && e.o?.sn === undefined && activeTransition === null && currentOptimisticLane === null && !snapshotCaptureActive && (!unflushedStaged || e._e === NOT_PENDING) && // A28, see readNodeFast
1045
+ let t = context;
1046
+ if (t?.$e) t = t.et;
1047
+ const n = e;
1048
+ // CARVE 1: `owner` was `el._firewall || el` — a store leaf's status,
1049
+ // height and error were read off its projection computed. (Typed loosely
1050
+ // as the `||` expression was: a signal's `_statusFlags`/`_height` read as
1051
+ // undefined and mask to 0 on every site below.)
1052
+ const E = e;
1053
+ if (typeof n.ee === "function") prepareComputed(e);
1054
+ // The verdict windows (`latest`, `isPending`; verdict.ts): one cold path,
1055
+ // off this one, present only while a window is open.
1056
+ if (verdict !== null) return verdict(e, t);
1057
+ if (!n.ee && e.m?.Le === undefined && !snapshotCaptureActive && (!unflushedStaged || e.Oe === NOT_PENDING) && // A28, see readNodeFast
1058
+ !(e.i & CONFIG_OVERRIDE) && // lanes: the slow path's one arm
1514
1059
  true) {
1515
- if (n && tracking) link(e, n);
1516
- // Committed visibility for children-forbidden readers and for stale
1517
- // readers of a foreign transaction's staged write — see readNodeFast.
1518
- return !n || e._e === NOT_PENDING || n.C & CONFIG_CHILDREN_FORBIDDEN || stale && heldFromStale(e, n) ? e.ce : (enterStagedRead(e),
1519
- e._e);
1520
- }
1521
- if (n && tracking) {
1522
- link(e, n, pendingCheckActive);
1523
- if (u.he) {
1524
- const t = queueFor(e);
1525
- if (u.rn >= t.ln) {
1526
- markNode(n);
1527
- markHeap(t);
1528
- updateIfNecessary(u);
1529
- }
1530
- // Fresh-pull readers (awaitable refresh's waiter) recompute a dirty
1531
- // source inline even when the height gate defers to the flush: the
1532
- // waiter must park on the re-ask's window (or serve its sync answer),
1533
- // never read the PRE-re-ask value as settled. Self-guarded: a clean
1534
- // node no-ops and updateIfNecessary refuses disposed nodes (#2983) —
1535
- // a dead target serves its last value, which is already quiescent.
1536
- else if (n.C & CONFIG_FRESH_READ) updateIfNecessary(u);
1537
- const i = u.rn;
1538
- // parent check is shallow, might need to be recursive
1539
- if (i >= n.rn && e._parent !== n) {
1540
- n.rn = i + 1;
1060
+ if (t) {
1061
+ if (tracking) link(e, t);
1062
+ // L2: a pass that reads something already in the future joins it —
1063
+ // the one mechanism behind "a write that reaches the future joins
1064
+ // it". Tracked or not (`untrack` is about dependencies, not about
1065
+ // which world a pass derives from — posture-store-parity S4/S5): a
1066
+ // derivation of a held write published mainline would tear. Frame
1067
+ // readers (children-forbidden, A32) see committed below and stay
1068
+ // out; a render effect outside a parking flush sees committed and
1069
+ // stays out too (rule 3, `frameRead`). The pass remembers it read the
1070
+ // future (REACTIVE_JOINED): a first pass that did is born held (A29),
1071
+ // wherever it was created. Lane work sees the screen (`frameRead`).
1072
+ if (e.i & CONFIG_HELD && !(t.i & CONFIG_CHILDREN_FORBIDDEN)) {
1073
+ if (frameRead(t, e)) return e.Ae;
1074
+ joinPass(t, e);
1541
1075
  }
1542
1076
  }
1077
+ // Committed visibility for untracked and children-forbidden readers.
1078
+ if (!t || e.Oe === NOT_PENDING || t.i & CONFIG_CHILDREN_FORBIDDEN) return e.Ae;
1079
+ // (A plain signal is never uninitialized — the carve-out is `serve`'s.)
1080
+ if (t.i & CONFIG_VERDICT && stagedScreen(t)) return e.Ae;
1081
+ stagedRead(t);
1082
+ return e.Oe;
1083
+ }
1084
+ // Rule 3: this read serves the committed value to a render effect outside a
1085
+ // parking flush (decided after the pull below — a pull can join).
1086
+ let u = false;
1087
+ if (t) {
1088
+ if (tracking) {
1089
+ link(e, t);
1090
+ if (E.ee) pullComputed(E, t);
1091
+ }
1092
+ // L2, as in the fast block above (tracked or not). A node born into the
1093
+ // future (uninitialized) has no committed value: every reader derives
1094
+ // from the future there, a render effect included (A29: a stale reader
1095
+ // of it cannot fall back to the committed frame and enters instead).
1096
+ if (e.i & CONFIG_HELD && !(e.i & CONFIG_OVERRIDE) && !(t.i & CONFIG_CHILDREN_FORBIDDEN)) {
1097
+ if (E.S & STATUS_UNINITIALIZED || !frameRead(t, e)) joinPass(t, e); else u = true;
1098
+ }
1099
+ }
1100
+ // Lanes: a lane's node (after the pull — the node is current). NOT_PENDING
1101
+ // falls through: lane work's read of a pending member throws like any.
1102
+ if (e.i & CONFIG_OVERRIDE) {
1103
+ const n = GlobalQueue.rt(t, e);
1104
+ if (n !== NOT_PENDING) return n;
1543
1105
  }
1544
- if (u.h & STATUS_PENDING) {
1545
- // A reader landing on a pending node throws — the reveal that discovered
1546
- // the flight holds on it (A15: observed async settles as one unit) — with
1547
- // one carve-out: a stale (render) reader of a node pending in some OTHER
1548
- // transaction keeps showing the node's committed value, no entanglement
1549
- // (parallel transactions; the reader is recorded for that transaction's
1550
- // commit replay, `heldFromStale`). The carve-out is sound only while the
1551
- // committed value is coherent with the visible frame, i.e. while the
1552
- // flight's inputs are themselves unpublished: the stamp alone does not
1553
- // say so (it is pending-node bookkeeping), so it is refused when the
1554
- // inputs are on screen — committed by a batch that left the flight in
1555
- // the air (CONFIG_INPUTS_PUBLISHED, #3305) or revealed through a lane
1556
- // (optimistic / latest, #3334) — and the reader holds instead. An
1557
- // UNINITIALIZED node has no committed value to show and holds too
1558
- // (firewall-backed store reads always did; plain memos since the #3043
1559
- // port): falling through served `undefined` as if settled and stranded
1560
- // the reader outside both transactions, so it never re-ran.
1561
- if (n && !(stale && !(u.h & STATUS_UNINITIALIZED) && !(u.C & CONFIG_INPUTS_PUBLISHED) && !(u.C & CONFIG_HAS_LANE && GlobalQueue.tt(u)) && heldFromStale(u, n))) {
1562
- // Per-lane suspension lives with the engine (a non-null lane implies it
1563
- // is installed): under a lane, only same-lane pending async without an
1564
- // active override throws — plus uninitialized sources regardless of
1565
- // lane (#3276); that check rides laneSuspends so floor bundles don't
1566
- // pay for it.
1567
- if (currentOptimisticLane === null || GlobalQueue.it(u)) {
1106
+ // A stale reader of a held flight (rule 3, A15's reveal corollary) is served
1107
+ // the committed value — coherent with the frame, whose inputs are the
1108
+ // committed ones too — and does not go pending on it. So is lane work
1109
+ // reading a flight that is not the lane's own (the parent's, or
1110
+ // mainline's): a lane breaks out of its parent's hold and waits only on
1111
+ // what derives from the guess (A31: "under a lane, a pending node on no
1112
+ // lane serves its committed value").
1113
+ // A verdict reader likewise (CONFIG_VERDICT): a frame reader sees the
1114
+ // screen, and for a flight the screen is the committed value — a render
1115
+ // effect keeps its DOM by throwing, a memo has no DOM and is handed the
1116
+ // value. `[isPending(x), x()]` reads `[true, stale]` in either order (A10)
1117
+ // — unless the flight committed beneath inputs already on screen
1118
+ // (`observeFlight`), or the reader was born into the flight's transaction
1119
+ // (#4010): it has no screen, and waits on the flight with its frame.
1120
+ if (E.S & STATUS_PENDING && !u && !(e.i & CONFIG_OVERRIDE) && !(E.S & STATUS_UNINITIALIZED)) {
1121
+ // A reader with no committed value yet sees the flight pending: a
1122
+ // boundary a lane mounts shows its fallback (A29's boundary exemption).
1123
+ if (passLane !== null && !(t?.S & STATUS_UNINITIALIZED)) u = true; else if (t !== null && t.i & CONFIG_VERDICT) u = GlobalQueue.It(t, E);
1124
+ }
1125
+ if (E.S & STATUS_PENDING && !u) {
1126
+ // A reader landing on a pending node throws; an untracked read of an
1127
+ // UNINITIALIZED node has no committed value to serve and throws too. A
1128
+ // children-forbidden reader (createTrackedEffect / onSettled) sees the
1129
+ // frame, not the graph (A32): the committed value, and the NotReady only
1130
+ // where there is none (uninitialized).
1131
+ if (t) {
1132
+ if (!(t.i & CONFIG_CHILDREN_FORBIDDEN) || E.S & STATUS_UNINITIALIZED) {
1568
1133
  // An untracked read of a pending node still re-runs its reader when
1569
1134
  // the node settles — unless nobody is reading (`spectating`, #3528)
1570
1135
  // or the reader is the node itself.
1571
- if (!tracking && !spectating && e !== n) link(e, n);
1572
- throw u.o?._;
1136
+ if (!tracking && !spectating && e !== t) link(e, t);
1137
+ throw E.m?.F;
1573
1138
  }
1574
- } else if (!n && u.h & STATUS_UNINITIALIZED &&
1575
- // An armed DERIVED override shields the uninitialized state from a
1576
- // read with no reader identity (#3648 follow-up, A18 (d)): a memo
1577
- // whose first landing rode its lane (asyncWrite's lane branch →
1578
- // `laneOverride`) has no `_value` — the flag stays set until the lane's
1579
- // commit promotes the override — yet the override IS the displayed
1580
- // value of a pending node (A18 (c), the screen keeps it until the
1581
- // commit), exactly as an initialized node re-deriving under its override
1582
- // displays it. "Uninitialized must suspend" (#3276, A19 exception 1) was
1583
- // written for a node with NOTHING to show; this node has the override.
1584
- // The arm fires in the body-end window: the source override superseded
1585
- // (A18 body-end, #3427), the memo re-deriving on the plain channel and
1586
- // held, its own slot still armed. Fall through to serve()'s override
1587
- // arm. Readers WITH identity are untouched: a tracked lane reader keeps
1588
- // the override (A17), an off-lane one suspends (#3651, `overrideRead`).
1589
- !(hasActiveOverride(e) && e.C & CONFIG_DERIVED_OVERRIDE)) {
1590
- throw u.o?._;
1139
+ } else if (E.S & STATUS_UNINITIALIZED) {
1140
+ throw E.m?.F;
1591
1141
  }
1592
1142
  }
1593
- // `owner` is the computed itself, or the firewall behind a store node —
1594
- // firewall-backed reads follow the same rules (memo parity, #2897 ruling):
1595
- // an errored derive throws for every late reader instead of silently
1596
- // serving node values (the seed, or last-good data after a failed refetch).
1597
- if (u.he && u.h & STATUS_ERROR) {
1143
+ // An errored derive throws for every late reader instead of silently
1144
+ // serving node values (memo parity, #2897 ruling).
1145
+ if (E.ee && E.S & STATUS_ERROR) {
1598
1146
  // Only a genuine reactive re-read may retry an errored async source:
1599
1147
  // - tracking: owned/tracked scope only (never events / `untrack` / effect side-effect phase)
1600
- // - !pendingCheckActive: an `isPending` probe observes the error, never refetches
1601
1148
  // - owner._time < clock: only on a later cycle than the one the error was found
1602
- if (tracking && !pendingCheckActive && u.be < clock) {
1603
- recompute(u);
1149
+ if (tracking && E.Ie < clock) {
1150
+ recompute(E);
1604
1151
  return read(e);
1605
- } else throw u.o?._;
1152
+ } else throw E.m?.F;
1606
1153
  }
1607
- if (snapshotCaptureActive && n && n.C & CONFIG_IN_SNAPSHOT_SCOPE) {
1608
- const t = e.o?.sn;
1609
- if (t !== undefined) {
1610
- const i = t === NO_SNAPSHOT ? undefined : t;
1611
- const u = e._e !== NOT_PENDING ? e._e : e.ce;
1612
- if (u !== i) n.oe |= REACTIVE_SNAPSHOT_STALE;
1613
- return i;
1154
+ if (snapshotCaptureActive && t && t.i & CONFIG_IN_SNAPSHOT_SCOPE) {
1155
+ const n = e.m?.Le;
1156
+ if (n !== undefined) {
1157
+ const E = n === NO_SNAPSHOT ? undefined : n;
1158
+ const u = e.Oe !== NOT_PENDING ? e.Oe : e.Ae;
1159
+ if (u !== E) t.R |= REACTIVE_SNAPSHOT_STALE;
1160
+ return E;
1614
1161
  }
1615
1162
  }
1616
- const l = serve(e, n, u, e.ce);
1617
- if (!n && u === e && typeof t.he === "function" && e.C & CONFIG_AUTO_DISPOSE && !(u.h & STATUS_PENDING) && !e.u &&
1618
- // An untracked read served a visible override returned before this
1619
- // sweep registration when the arm was inline; keep that.
1620
- !visibleOverride(e)) {
1163
+ if (u) return e.Ae;
1164
+ const s = serve(e, t);
1165
+ if (!t && typeof n.ee === "function" && e.i & CONFIG_AUTO_DISPOSE && !(E.S & STATUS_PENDING) && !e.t) {
1621
1166
  // Deferred, not inline (#3078): an inline unobserved() here made untracked
1622
1167
  // reads destructive — dispose on this read, full revival recompute on the
1623
1168
  // next — so consecutive reads could answer differently with no write in
1624
- // between (the revival samples the ambient transition/lane context).
1169
+ // between.
1625
1170
  // The sweep at flush finalization re-validates and reclaims; schedule()
1626
1171
  // guarantees that flush happens even if nothing else is queued.
1627
1172
  dormantNodes.add(e);
1628
1173
  schedule();
1629
1174
  }
1630
- return l;
1175
+ return s;
1631
1176
  }
1632
1177
 
1633
1178
  /**
1634
1179
  * Rule 1, the one slow implementation (DESIGN-CONSOLIDATION move 3b, step
1635
1180
  * 6c): the value a reader `c` (null = untracked, no pass) is served from
1636
- * `el`, whose committed value is `committed` — the node's own `_value` for
1637
- * a signal or memo, the BACKING for a store property node (single-home rule,
1638
- * O6: committed truth lives in the backing and a node's `_value` is never
1639
- * served for one). Called by read()'s slow tail and by the store's untracked
1640
- * node path (nodeValue); the fast paths (readNodeFast, read's fast block)
1641
- * keep their trivial ternary by design (perf, see the doc). Arms, in order:
1642
- * - the override (A17), routed through the engine for a tracked reader under
1643
- * a lane or a supersession (A18), an authoritative reader marked instead;
1644
- * - the lane entanglement gate (committed, recorded for replay);
1645
- * - a node born held has nothing for an untracked reader (A19 exception 1);
1181
+ * `el`. Called by read()'s slow tail; the fast path (read's fast block) keeps
1182
+ * its trivial ternary by design (perf, see the doc). Arms, in order:
1183
+ * - a node born staged has nothing for an untracked reader (A19 exception 1);
1646
1184
  * - an unflushed write serves committed and re-runs the reader in the
1647
1185
  * carrying flush (A28);
1648
- * - readerSeesCommitted, else the staged value and the transaction (A29).
1649
- */ function serve(e, n, t, i) {
1650
- if (hasActiveOverride(e)) {
1651
- // A17: the override IS the value for every reader — except an authoritative
1652
- // reader (until()'s predicate carries CONFIG_AUTHORITATIVE_READ): it must
1653
- // observe independently-arriving truth, and serving it the caller's own
1654
- // tentative write would trivially satisfy the predicate. The bit is checked
1655
- // on the reading computation itself, so a shared computed the predicate
1656
- // pulls recomputes as ITSELF (context = the memo, no bit) under the normal
1657
- // view. Fall through to normal value selection (staged `_pendingValue` is
1658
- // authoritative — optimism never lives there); the sticky mark makes the
1659
- // A17-silent "landing equals override" paths notify this node's subs so
1660
- // the reader re-runs when truth arrives.
1661
- // A28 (5): an optimistic write written this tick, outside a flush, is not
1662
- // yet the active override — fall through to the normal selection (the
1663
- // authoritative mark below still applies: until() must wake on landing).
1664
- if (!(n && n.C & CONFIG_AUTHORITATIVE_READ) && !unflushedOverride(e)) {
1665
- // A tracked read of an override is the engine's selection (a lane or a
1666
- // supersession implies the engine): a render effect OFF the override's
1667
- // held lane sees the committed value (#3460, lanes mirror transitions);
1668
- // a node whose own source answered with a DIFFERENT value hands a
1669
- // tracked reader the staged truth (A18 supersession, #3331). Untracked
1670
- // reads display the override.
1671
- if (n && e.C & (CONFIG_HAS_LANE | CONFIG_OVERRIDE_SUPERSEDED)) return GlobalQueue.ut(e, n);
1672
- return unwrapOverride(e.o?.Fe);
1673
- }
1674
- e.C |= CONFIG_AUTHORITATIVE_OBSERVED;
1675
- }
1676
- // Entanglement gate: a reader recomputing under an optimistic lane that reads
1677
- // a pending mid-transition write sees the committed value. Projection-store
1678
- // manual writes use the firewall's manual-write flag to opt into this path.
1679
- // Async drivers are not under an optimistic lane and so bypass this, reading
1680
- // _pendingValue for correct fetching. The sub is recorded for replay at commit
1681
- // so it re-runs with the new committed view. (Gate details live with the
1682
- // engine — a non-null lane implies it is installed.)
1683
- if (currentOptimisticLane !== null && activeTransition !== null && n !== null && GlobalQueue.lt(e, t, n)) {
1684
- return i;
1186
+ * - readerSeesCommitted, else the staged value.
1187
+ */ function serve(e, t) {
1188
+ // A node born staged (recompute) has a staged value and no committed one:
1189
+ // an untracked reader has nothing to serve and holds (A19 exception 1) — a
1190
+ // bookkeeping read (`spectate`) likewise, and a children-forbidden reader
1191
+ // (A32: the frame, which has nothing here).
1192
+ if (e.Oe !== NOT_PENDING && e.S & STATUS_UNINITIALIZED && (!t || spectating || t.i & CONFIG_CHILDREN_FORBIDDEN)) throw new NotReadyError(null);
1193
+ const n = t && unflushedStaged ? unflushedValue(e) : NOT_PENDING;
1194
+ if (n !== NOT_PENDING) {
1195
+ markLateLinker(t);
1196
+ return n;
1685
1197
  }
1686
- // In optimistic lane context, return _value for optimistic/lane-assigned signals
1687
- // and for regular signals in stale mode (render effects). Non-stale readers (user
1688
- // effects) see _pendingValue so that latest() and direct reads stay consistent.
1689
- // (The lane-context clause lives with the engine.) Children-forbidden readers
1690
- // (createTrackedEffect / onSettled callbacks) get committed visibility — see
1691
- // readNodeFast (#3006).
1692
- // A node born held (recompute) has a staged value and no committed one: a
1693
- // stale reader cannot fall back to the committed frame, so it takes the
1694
- // staged value and enters like a tracked reader (A29); an untracked reader
1695
- // has nothing to serve and holds (A19 exception 1) — a bookkeeping read
1696
- // (`spectate`) likewise: it derives nothing, so it cannot enter, and a
1697
- // held-only value is simply not ready to it.
1698
- const u = e._e !== NOT_PENDING && (e.h & STATUS_UNINITIALIZED) !== 0;
1699
- if (u && (!n || spectating)) throw new NotReadyError(null);
1700
- const l = n && unflushedStaged ? unflushedValue(e, i) : NOT_PENDING;
1701
- if (l !== NOT_PENDING) {
1702
- markLateLinker(n);
1703
- if (pendingCheckActive) GlobalQueue.rt(e, l);
1704
- return l;
1705
- }
1706
- const r = readerSeesCommitted(e, n, t, u) ? i : (enterStagedRead(e), e._e);
1707
- // Record that this isPending() probe observed the fresh pending value, so
1708
- // the probe doesn't pair "pending" with the new value (#2831).
1709
- if (pendingCheckActive) GlobalQueue.rt(e, r);
1710
- return r;
1198
+ if (readerSeesCommitted(e, t)) return e.Ae;
1199
+ if (t.i & CONFIG_VERDICT && !(e.S & STATUS_UNINITIALIZED) && stagedScreen(t)) return e.Ae;
1200
+ stagedRead(t, e);
1201
+ return e.Oe;
1711
1202
  }
1712
1203
 
1713
- /** A28 — a rewrite of a HELD node outside a flush keeps the staged value the
1714
- * last flush left, for latest()/verdicts, until the next flush carries it
1715
- * (stash, cleared at that flush). Cold: only stamped nodes reach here. */ function stashHeldRewrite(e) {
1716
- if (globalQueue.Jn) return;
1717
- const n = ext(e);
1718
- if (n.bn === NOT_PENDING) {
1719
- n.bn = e._e;
1720
- unflushedRewrites.push(e);
1721
- unflushedStaged = true;
1204
+ /** A pass read a staging of this flush: it derives from what the flush may
1205
+ * yet hold (REACTIVE_STAGED_READ — the seam decides). Lane work too (plan sec. 28, a
1206
+ * lane sees the screen plus its own guesses): the staging is the screen if
1207
+ * the frame commits — one pass, the common case — and a held write if it
1208
+ * parks, which the seam repairs (`stagedReaders`): the pass re-derives on
1209
+ * the committed world and its lane's runs wait that round, so the held
1210
+ * write never shows through the lane. A verdict lane's work likewise: the
1211
+ * lane holds verdicts, not the frame's other stagings (#3851) — except a
1212
+ * verdict reader, which answered for itself (the lane seam, lanes.ts). Not
1213
+ * a read of a node born staged at all: it has no committed value to
1214
+ * re-derive on, and a re-run would read the same staging and re-queue every
1215
+ * round (#3969: nor does it make a later lane read a re-derive, `enterLane`). */ function stagedRead(e, t) {
1216
+ if (!(t?.S & STATUS_UNINITIALIZED)) {
1217
+ e.R |= REACTIVE_STAGED_READ;
1218
+ if (passLane !== null) stagedReaders.push(e);
1722
1219
  }
1723
1220
  }
1724
1221
 
1222
+ /** A10 for a staged node: a verdict reader (the pass entered a window) that
1223
+ * also reads a frame's proposal plainly sees the screen — `[latest(q), q()]`
1224
+ * is `[b, a]` — once the frame has a transaction, and re-derives when it
1225
+ * lands: at this very seam if nothing holds it (the stale run is void,
1226
+ * #3322 — one run, on the committed world), or with the hold. Before any
1227
+ * transaction the answer is the seam's and the reader watches it
1228
+ * (verdict.ts). A node born into the future has no screen: its staging is
1229
+ * its only value (A29). Nor has a reader born into the flush's transaction
1230
+ * (`bornInto`, #4010): its branch exists only there. */ function stagedScreen(e) {
1231
+ if (flushTransaction === null || bornInto(e, flushTransaction)) return false;
1232
+ staleReader(e, flushTransaction);
1233
+ return true;
1234
+ }
1235
+
1725
1236
  /** A28 (4) — written inside a recompute that runs OUTSIDE a flush (a
1726
1237
  * creation-time compute): promoted at that pass's end, visible to the rest of
1727
1238
  * the block. Cold: only contextual writes reach here. */ function notePromotedWrite(e) {
1728
- if (globalQueue.Jn || e.C & CONFIG_PROMOTED) return;
1729
- e.C |= CONFIG_PROMOTED;
1239
+ if (globalQueue.X || e.i & CONFIG_PROMOTED) return;
1240
+ e.i |= CONFIG_PROMOTED;
1730
1241
  promotedWrites.push(e);
1731
1242
  }
1732
1243
 
1733
- /** Cold half of setSignal's snapshot arm (see there): first write during
1734
- * capture to a plain signal without a live snapshot records the pre-write
1735
- * value. Only plain user signals qualify. Computeds reach setSignal from an
1244
+ /** Cold half of setSignal's snapshot arm (see there), shared with a store
1245
+ * container's structural notify: first write during capture to a plain
1246
+ * signal without a live snapshot records the pre-write value. Only plain
1247
+ * user signals qualify. Computeds reach setSignal from an
1736
1248
  * async landing (asyncWrite), and a value arriving from async during the pass
1737
1249
  * REVEALS — the creation-time arm skips pending computeds for the same
1738
1250
  * reason. Firewall leaves belong to a projection whose compute is the tree's
1739
- * own work, captured (or deliberately not) at creation. */ function captureWriteSnapshot(e, n) {
1740
- if (e.C & CONFIG_NO_SNAPSHOT || e.he !== undefined || e.De || e.o?.sn !== undefined) return;
1741
- ext(e).sn = n === undefined ? NO_SNAPSHOT : n;
1742
- e.C |= CONFIG_HAS_SNAPSHOT;
1251
+ * own work, captured (or deliberately not) at creation. */ function captureWriteSnapshot(e, t) {
1252
+ if (e.i & CONFIG_NO_SNAPSHOT || e.ee !== undefined || e.m?.Le !== undefined) return;
1253
+ ext(e).Le = t === undefined ? NO_SNAPSHOT : t;
1254
+ e.i |= CONFIG_HAS_SNAPSHOT;
1743
1255
  snapshotSources.add(e);
1744
1256
  }
1745
1257
 
1746
- function setSignal(e, n) {
1747
- // A write to a held node is a second proposal on a contested node (A34, #3494):
1748
- // the writer's tick reveals with the hold, the same value or another. Inside
1749
- // a flush the round enters now; from mainline the entry waits for the next
1750
- // flush's start (batchJoins) — entering here left activeTransition set for
1751
- // the rest of the caller's block, so a memo created after the write became
1752
- // the transaction's instead of mainline's (A28, A29). Before the equality
1753
- // gate below: repeating the held value proposes it too — and that repeat
1754
- // leaves through the gate, so the join schedules its own flush here; left
1755
- // for the next flush to find, it adopted an unrelated tick (#3519 review).
1756
- // A held DERIVATION is not a proposal (A34 amendment, #3612): the user
1757
- // setters of derived nodes (setMemo, the derived store's setter) classify
1758
- // the write before it reaches here — see heldDerivation — and re-derive
1759
- // under the hold instead of masking; the join stands either way.
1760
- if (e.we && activeTransition !== e.we) {
1761
- if (globalQueue.Jn) globalQueue.initTransition(e.we); else {
1762
- batchJoins.push(e.we);
1763
- // dupes: a bare return in initTransition
1764
- schedule();
1765
- }
1766
- }
1767
- // The optimistic write path lives with the engine: only optimisticSignal /
1768
- // optimisticComputed callers and optimistic store nodes carry an
1769
- // _overrideValue slot (flagged by CONFIG_OPTIMISTIC — a masked read of the
1770
- // always-present config instead of a missing-property probe), and every
1771
- // module that installs one installs the engine first.
1772
- if (e.C & CONFIG_OPTIMISTIC) {
1773
- if (!projectionWriteActive) return GlobalQueue.ot(e, n);
1774
- // An authoritative store landing on an override-covered node: the store
1775
- // twin of asyncWrite's override branch, decided by the engine (#3331).
1776
- const t = e.o?.Fe;
1777
- if (t !== undefined && t !== NOT_PENDING) return GlobalQueue.st(e, n);
1778
- }
1779
- const t = e._e === NOT_PENDING ? e.ce : e._e;
1780
- if (typeof n === "function") n = n(t);
1258
+ function setSignal(e, t) {
1259
+ const n = e.Oe === NOT_PENDING ? e.Ae : e.Oe;
1260
+ if (typeof t === "function") t = t(n);
1261
+ // Lanes: a write landing on a guess is its truth (A18 — an async guess's
1262
+ // own landing), judged against the guess; one landing on a lane's
1263
+ // derivation (its flight) is the lane's staging (lanes.ts).
1264
+ if (e.i & CONFIG_OVERRIDE) return GlobalQueue.Nt(e, t);
1265
+ // L2 (T4) / A34 (1): a write to a node whose staged state is held is a
1266
+ // second proposal for the slot — the same value again or another — and
1267
+ // the two cannot finish at different times: the writer's tick joins the
1268
+ // hold, before the equality gate.
1269
+ if (e.i & CONFIG_HELD) joinFuture(txOf(e));
1781
1270
  // Uninitialized check first: the first commit has no previous value, so the
1782
1271
  // user comparator must not run against `undefined` (matches recompute).
1783
- const i = !!(e.h & STATUS_UNINITIALIZED) || !e.xe || !e.xe(t, n);
1784
- if (!i) return n;
1272
+ const E = !!(e.S & STATUS_UNINITIALIZED) || !e.ye || !e.ye(n, t);
1273
+ if (!E) return t;
1785
1274
  // A write during hydration's snapshot capture to a source that has no
1786
1275
  // snapshot — created BEFORE capture began (module-level state: an identity
1787
1276
  // minted from onSettled in the pass, a preference read from storage) —
@@ -1791,94 +1280,58 @@ function setSignal(e, n) {
1791
1280
  // pass whose DOM writes are skipped, and a component rendered later in the
1792
1281
  // pass reads a value the server never had. Store leaves written here are
1793
1282
  // plain signals and qualify the same way.
1794
- if (snapshotCaptureActive) captureWriteSnapshot(e, t);
1795
- const u = e._e !== NOT_PENDING;
1283
+ if (snapshotCaptureActive) captureWriteSnapshot(e, n);
1284
+ const u = e.Oe !== NOT_PENDING;
1796
1285
  if (!u) queuePendingNode(e);
1797
- // A28 arms, gated on the loads the write already pays for (a plain ambient
1798
- // rewrite outside a flush — the write-loop shape — costs `_transition` and
1799
- // `context` here and nothing else; the arms themselves are cold helpers so
1800
- // setSignal stays within every setter's inlining budget, ~300 B bytecode).
1801
- else if (e.we !== null) stashHeldRewrite(e);
1802
- e._e = n;
1286
+ // A28: a held node rewritten outside a flush keeps its flushed staging
1287
+ // for every reader until the flush that carries the rewrite.
1288
+ else if (e.i & CONFIG_HELD && !globalQueue.X) stashFlushed(e);
1289
+ e.Oe = t;
1803
1290
  if (context !== null) notePromotedWrite(e);
1804
- // syncCompanions only pokes _pendingSignal/_latestValueComputed — with
1805
- // neither companion present the call is a guaranteed no-op (companions are
1806
- // only ever created, never removed, and creating one installs the hook and
1807
- // sets CONFIG_HAS_COMPANIONS — one masked read replaces two optional-field
1808
- // probes on every write).
1809
- if (e.C & CONFIG_HAS_COMPANIONS && GlobalQueue.Ve !== null) {
1810
- GlobalQueue.Ve(e, n);
1811
- // A28 (2): the verdict computed here is the PRE-flush one (an unflushed
1812
- // write is not pending); the flush that carries the write re-syncs so
1813
- // the companions mirror the flushed world. Only companion nodes pay.
1814
- if (!globalQueue.Jn) unflushedCompanions.push(e);
1815
- }
1816
1291
  // _time is a computed-only slot (§12e): writing it on a signal would fork
1817
1292
  // the lean shape. Every read site is computed-typed.
1818
- if (e.he !== undefined) e.be = clock;
1293
+ if (e.ee !== undefined) e.Ie = clock;
1819
1294
  // Staged-rewrite fast path (§12d): a re-write to a node whose subscribers
1820
1295
  // were already walked — and where nothing has recomputed or linked since
1821
1296
  // (epoch) — re-stages the value and stops. The walk is idempotent (subs
1822
- // marked, heap entries flag-guarded, effects queued once); lane and reask
1823
- // contexts change what a walk MEANS, so they always walk.
1824
- if (u && e.vn === notifyEpoch && currentOptimisticLane === null && !reaskArmed) return n;
1297
+ // marked, heap entries flag-guarded, effects queued once).
1298
+ if (u && e.qe === notifyEpoch) return t;
1825
1299
  insertSubs(e);
1826
1300
  schedule();
1827
- return n;
1301
+ return t;
1828
1302
  }
1829
1303
 
1830
1304
  /**
1831
- * Suppresses automatic recomputation of `el` until the scheduler drains. Used
1832
- * when a manual write should win over dependency changes queued in the same
1833
- * tick. The MANUAL_WRITE flag is cleared by the pending-node drain; projection
1834
- * computeds don't commit values, but they still need the same end-of-tick
1835
- * cleanup point.
1836
- */ function suppressComputedRecompute(e) {
1837
- deleteFromHeap(e, queueFor(e));
1838
- if (!(e.oe & REACTIVE_MANUAL_WRITE) && e._e === NOT_PENDING) {
1839
- queuePendingNode(e);
1305
+ * User-facing setter for the memo form of `createSignal(fn)`. A write applies
1306
+ * first, then derivations re-run (A34 as amended 2026-10-01, rule B; core
1307
+ * R31): the write stages like any other, and a source change — in this flush
1308
+ * or a later one, under a hold or on mainline — re-derives with the staged
1309
+ * write as `prev`. The write never refuses the re-run (the frame-scoped mask
1310
+ * of #3733/#3740 and #2692's "write wins the tick" before it are gone; call
1311
+ * order inside a flush was never observable) and on its own never causes one.
1312
+ * REACTIVE_MANUAL_WRITE only marks the staging as a user proposal — the A34
1313
+ * (1)/(3) discriminator — until a pass re-derives it or the commit lands it.
1314
+ */ function setMemo(e, t) {
1315
+ // A34 (3), #3612: is `el`'s staging a derivation another transaction holds
1316
+ // — held, not a manual proposal, and the writer not that transaction
1317
+ // (mainline, or a flush joined elsewhere)? A held pass result is nobody's
1318
+ // proposal: a setter reaching it composes on the committed frame it was
1319
+ // written against and becomes `prev` for the hold's re-derivation, instead
1320
+ // of replacing it. A write made under the hold carries the mark and keeps
1321
+ // (1)'s last-write-wins. Only the user setter asks; an async landing
1322
+ // writing a held node is the transaction's own work.
1323
+ const n = e.i & CONFIG_HELD && !(e.R & REACTIVE_MANUAL_WRITE) && txOf(e) !== flushTransaction;
1324
+ // The writer read the committed frame; its updater composes on it.
1325
+ if (n && typeof t === "function") t = t(e.Ae);
1326
+ const E = setSignal(e, t);
1327
+ if (n) {
1328
+ // The one write that re-runs: the staging it replaced was derived from
1329
+ // inputs the writer never saw, so the hold re-derives over the write.
1330
+ e.R = e.R & ~REACTIVE_CHECK | REACTIVE_DIRTY;
1331
+ insertIntoHeap(e, dirtyQueue);
1840
1332
  schedule();
1841
- }
1842
- e.oe = e.oe & -4 | REACTIVE_MANUAL_WRITE;
1843
- e.ct = clock;
1844
- }
1845
-
1846
- /** A34 amendment (#3612) — is `el`'s staging a DERIVATION another transaction
1847
- * holds: stamped by a transaction that is not the writer's, and not a manual
1848
- * proposal (the mask, on the node or — a store leaf — its firewall)? #2692's
1849
- * "manual write wins" is a rule for one synchronous frame; across a hold the
1850
- * held pass result is nobody's proposal, and a user setter reaching it
1851
- * composes on the committed frame it was written against and becomes `prev`
1852
- * for the transaction's re-derivation (rederiveHeld) instead of replacing it.
1853
- * Writes made under the holding transaction masked the node and keep
1854
- * A34(1)'s last-write-wins. Only the user setters ask; an async landing or a
1855
- * companion writing a stamped node is the transaction's own work. */ function heldDerivation(e) {
1856
- return e.we !== null && activeTransition !== e.we && !((e.De || e).oe & REACTIVE_MANUAL_WRITE);
1857
- }
1858
-
1859
- /** The held-derivation write's second half: the node re-derives under its
1860
- * hold with the written staging as the pass's `prev`. Nothing is masked. The
1861
- * write took the A34 join (setSignal), which scheduled the flush that drains
1862
- * this; recompute re-enters the stamp. */ function rederiveHeld(e) {
1863
- e.oe |= REACTIVE_DIRTY;
1864
- // over CHECK: the heap visit recomputes, not re-checks
1865
- enqueueSub(e);
1866
- }
1867
-
1868
- /**
1869
- * User-facing setter for the memo form of `createSignal(fn)`. Behaves like
1870
- * `setSignal`, but also cancels any pending recompute of the memo so the
1871
- * manual value wins over a value that would otherwise be produced by an
1872
- * upstream change in the same tick. Across a hold the write is not a
1873
- * proposal: a memo another transaction holds as a pass result composes on the
1874
- * committed value and re-derives under the hold with the write as `prev`
1875
- * (A34 amendment, #3612; heldDerivation).
1876
- */ function setMemo(e, n) {
1877
- const t = heldDerivation(e);
1878
- if (t && typeof n === "function") n = n(e.ce);
1879
- const i = setSignal(e, n);
1880
- t ? rederiveHeld(e) : suppressComputedRecompute(e);
1881
- return i;
1333
+ } else if (e.Oe !== NOT_PENDING) e.R |= REACTIVE_MANUAL_WRITE;
1334
+ return E;
1882
1335
  }
1883
1336
 
1884
1337
  /**
@@ -1899,27 +1352,23 @@ function setSignal(e, n) {
1899
1352
  * setTimeout(() => runWithOwner(owner, fn), ms);
1900
1353
  * }
1901
1354
  * ```
1902
- */ function runWithOwner(e, n) {
1903
- const t = context;
1904
- const i = tracking;
1355
+ */ function runWithOwner(e, t) {
1356
+ const n = context;
1357
+ const E = tracking;
1905
1358
  context = e;
1906
1359
  tracking = false;
1907
1360
  try {
1908
- return n();
1361
+ return t();
1909
1362
  } finally {
1910
- context = t;
1911
- tracking = i;
1363
+ context = n;
1364
+ tracking = E;
1912
1365
  }
1913
1366
  }
1914
1367
 
1915
- function staleValues(e, n = true) {
1916
- const t = stale;
1917
- stale = n;
1368
+ function staleValues(e, t = true) {
1918
1369
  try {
1919
1370
  return e();
1920
- } finally {
1921
- stale = t;
1922
- }
1371
+ } finally {}
1923
1372
  }
1924
1373
 
1925
1374
  /**
@@ -1927,42 +1376,19 @@ function staleValues(e, n = true) {
1927
1376
  * it validates the target, marks through here, then builds the quiescence
1928
1377
  * promise on the resolve()/until() effect machinery). Flags the node's next
1929
1378
  * recompute as a quiet re-ask and schedules it; no-ops for non-derived or
1930
- * disposed targets and for same-tick manual writes.
1379
+ * disposed targets.
1931
1380
  */ function markRefresh(e) {
1932
- if (typeof e.he === "function" && !(e.oe & REACTIVE_DISPOSED)) {
1933
- if (e.oe & REACTIVE_MANUAL_WRITE) {
1934
- // A manual write in the CURRENT tick wins over the refresh (#2692).
1935
- // A mask stamped in an earlier tick only survives because a
1936
- // transaction (action) is holding the pending drain open; there the
1937
- // refresh is a later, explicit re-ask and lifts the mask — otherwise
1938
- // any setStore early in an action silently swallows every refresh()
1939
- // for the rest of the transaction (#3026).
1940
- if (e.ct === clock) return;
1941
- e.oe &= ~REACTIVE_MANUAL_WRITE;
1942
- // The lift falls through to the re-ask classification below. The held
1943
- // write's value change already rides the transaction; the refetch it
1944
- // asks for is the same question with unchanged inputs. Skipping the
1945
- // mark here classified that refetch as a NEW question, which pends
1946
- // every leaf (3.1) — an action doing setStore + yield + refresh(store)
1947
- // lit up every sibling row, and affects() could not narrow it (a mark
1948
- // only turns pending on). Same-question motion stays silent (3.4);
1949
- // the written slot and any declared mark carry the pending instead.
1950
- }
1951
- // A refresh with no value-change dirt already queued is a re-ask of the
1952
- // same question: mark it so the recompute classifies any resulting
1953
- // pending window as quiet (not pending). If the node is already dirty
1954
- // from a real input change, the question changed — don't mark.
1955
- // REACTIVE_IN_HEAP counts as dirt: insertSubs schedules subscribers by
1956
- // heap insertion alone (no DIRTY/CHECK flag), so a same-batch value
1957
- // change followed by refresh() must not be laundered into a quiet re-ask.
1958
- if (!(e.oe & (REACTIVE_DIRTY | REACTIVE_CHECK | REACTIVE_IN_HEAP))) {
1959
- e.oe |= REACTIVE_REASK;
1960
- armReaskClear();
1961
- }
1962
- e.oe = e.oe & ~REACTIVE_CHECK | REACTIVE_DIRTY;
1963
- insertIntoHeap(e, queueFor(e));
1381
+ if (typeof e.ee === "function" && !(e.R & REACTIVE_DISPOSED)) {
1382
+ // A manual write never refuses the re-run (A34 rule B): a refresh in the
1383
+ // write's own frame re-asks with the write as `prev`, like any source
1384
+ // change (#3026 generalized).
1385
+ // The re-ask of an answered question (A19 exc. 2): the next pass, going
1386
+ // pending, classifies its flight quiet for the verdict. Not when an
1387
+ // input already changed this tick — that pass asks a new question.
1388
+ e.R = e.R & ~REACTIVE_CHECK | REACTIVE_DIRTY | (e.R & (REACTIVE_DIRTY | REACTIVE_CHECK | REACTIVE_IN_HEAP) ? 0 : REACTIVE_REASK);
1389
+ insertIntoHeap(e, dirtyQueue);
1964
1390
  schedule();
1965
1391
  }
1966
1392
  }
1967
1393
 
1968
- export { READ_SLOW, clearSnapshots, computed, context, createEffectNode, currentOptimisticLane, effectStatusNotify, enterStagedRead, ext, hasActiveOverride, heldDerivation, installAuthoritativeRead, isEqual, latestReadActive, markLateLinker, markRefresh, markSnapshotScope, markUnflushedStaged, notifyAuthoritativeObservers, notifyOnLane, optimisticComputed, optimisticSignal, ownsHold, pendingCheckActive, prepareComputed, read, readNodeFast, readerSeesCommitted, recompute, recordStaleReplay, rederiveHeld, releaseSnapshotScope, resyncUnflushedCompanions, runWithOwner, serve, setContextInternal, setEffectStatusNotify, setLatestReadActive, setMemo, setPendingCheckActive, setSignal, setSlotUnobserved, setSnapshotCapture, signal, slotSignal, slotUnobservedHook, snapshotCaptureActive, snapshotSources, spectate, spectating, stale, staleValues, statusNotifierOf, suppressComputedRecompute, tracking, unflushed, unflushedCompanions, unflushedOverride, unflushedStaged, unflushedValue, unlinkFirewallChild, untrack, visibleOverride };
1394
+ export { bornInto, captureWriteSnapshot, clearSnapshots, computed, context, createEffectNode, effectStatusNotify, ext, isEqual, joinPass, markLateLinker, markRefresh, markSnapshotScope, markUnflushedStaged, notePromotedWrite, prepareComputed, pullComputed, read, readerSeesCommitted, recompute, releaseSnapshotScope, resyncUnflushedCompanions, runWithOwner, serve, setEffectStatusNotify, setMemo, setSignal, setSnapshotCapture, setVerdict, signal, slotSignal, snapshotCaptureActive, snapshotSources, spectating, stagedRead, stagedScreen, staleValues, stashFlushed, statusNotifierOf, tracking, unflushedStaged, unflushedValue, untrack, verdict };