@solidjs/signals 2.0.0-rc.2 → 2.0.0-rc.20

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 (198) hide show
  1. package/dist/dev-shared.js +6505 -0
  2. package/dist/dev.attribution.js +3484 -0
  3. package/dist/dev.js +10227 -9508
  4. package/dist/observe/affects-marks.js +86 -0
  5. package/dist/observe/affects.js +112 -0
  6. package/dist/observe/attribution.js +7 -0
  7. package/dist/observe/boundaries.js +696 -0
  8. package/dist/observe/core/action.js +182 -0
  9. package/dist/observe/core/async.js +824 -0
  10. package/dist/observe/core/attribution-costs.js +66 -0
  11. package/dist/observe/core/attribution-feedback.js +280 -0
  12. package/dist/observe/core/attribution-hooks.js +83 -0
  13. package/dist/observe/core/attribution-queries.js +43 -0
  14. package/dist/observe/core/attribution.js +3079 -0
  15. package/dist/observe/core/constants.js +269 -0
  16. package/dist/observe/core/context.js +63 -0
  17. package/dist/observe/core/copies.js +10 -0
  18. package/dist/observe/core/core.js +1707 -0
  19. package/dist/observe/core/dev.js +394 -0
  20. package/dist/observe/core/effect.js +193 -0
  21. package/dist/observe/core/error-hooks.js +71 -0
  22. package/dist/observe/core/error.js +82 -0
  23. package/dist/observe/core/external.js +97 -0
  24. package/dist/observe/core/graph.js +162 -0
  25. package/dist/observe/core/heap.js +124 -0
  26. package/dist/observe/core/identities.js +62 -0
  27. package/dist/observe/core/invariants.js +12 -0
  28. package/dist/observe/core/lanes.js +972 -0
  29. package/dist/observe/core/owner.js +371 -0
  30. package/dist/observe/core/scheduler.js +1157 -0
  31. package/dist/observe/core/truth.js +47 -0
  32. package/dist/observe/core/verdict.js +470 -0
  33. package/dist/observe/deferred.js +186 -0
  34. package/dist/observe/flatten.js +79 -0
  35. package/dist/observe/from.js +248 -0
  36. package/dist/observe/index.js +71 -0
  37. package/dist/observe/map.js +593 -0
  38. package/dist/observe/reveal.js +212 -0
  39. package/dist/observe/signals.js +717 -0
  40. package/dist/observe/store/affects.js +142 -0
  41. package/dist/observe/store/edges.js +39 -0
  42. package/dist/observe/store/hooks.js +16 -0
  43. package/dist/observe/store/index.js +47 -0
  44. package/dist/observe/store/optimistic.js +713 -0
  45. package/dist/observe/store/projection.js +345 -0
  46. package/dist/observe/store/reconcile.js +281 -0
  47. package/dist/observe/store/store.js +2127 -0
  48. package/dist/observe/store/storePath.js +103 -0
  49. package/dist/observe/store/target.js +46 -0
  50. package/dist/observe/store/types.js +83 -0
  51. package/dist/observe/store/utils.js +1063 -0
  52. package/dist/prod/affects-marks.js +86 -0
  53. package/dist/prod/affects.js +98 -112
  54. package/dist/prod/attribution.js +45 -0
  55. package/dist/prod/boundaries.js +578 -468
  56. package/dist/prod/core/action.js +92 -59
  57. package/dist/prod/core/async.js +522 -336
  58. package/dist/prod/core/attribution-hooks.js +3 -0
  59. package/dist/prod/core/constants.js +168 -44
  60. package/dist/prod/core/context.js +12 -16
  61. package/dist/prod/core/copies.js +10 -0
  62. package/dist/prod/core/core.js +1345 -627
  63. package/dist/prod/core/dev.js +123 -1
  64. package/dist/prod/core/effect.js +113 -80
  65. package/dist/prod/core/error-hooks.js +71 -0
  66. package/dist/prod/core/error.js +18 -4
  67. package/dist/prod/core/external.js +6 -7
  68. package/dist/prod/core/graph.js +119 -72
  69. package/dist/prod/core/heap.js +85 -109
  70. package/dist/prod/core/identities.js +62 -0
  71. package/dist/prod/core/invariants.js +1 -32
  72. package/dist/prod/core/lanes.js +931 -132
  73. package/dist/prod/core/owner.js +192 -129
  74. package/dist/prod/core/scheduler.js +948 -642
  75. package/dist/prod/core/truth.js +47 -0
  76. package/dist/prod/core/verdict.js +409 -420
  77. package/dist/prod/deferred.js +184 -0
  78. package/dist/prod/flatten.js +77 -0
  79. package/dist/prod/from.js +248 -0
  80. package/dist/prod/index.js +40 -16
  81. package/dist/prod/map.js +481 -211
  82. package/dist/prod/reveal.js +212 -0
  83. package/dist/prod/signals.js +401 -84
  84. package/dist/prod/store/affects.js +142 -0
  85. package/dist/prod/store/edges.js +39 -0
  86. package/dist/prod/store/hooks.js +16 -0
  87. package/dist/prod/store/index.js +27 -18
  88. package/dist/prod/store/optimistic.js +707 -0
  89. package/dist/prod/store/projection.js +331 -0
  90. package/dist/prod/store/reconcile.js +281 -0
  91. package/dist/prod/store/store.js +1952 -267
  92. package/dist/prod/store/storePath.js +1 -1
  93. package/dist/prod/store/target.js +43 -0
  94. package/dist/prod/store/types.js +83 -0
  95. package/dist/prod/store/utils.js +997 -138
  96. package/dist/types/affects-marks.d.ts +14 -0
  97. package/dist/types/affects.d.ts +12 -30
  98. package/dist/types/attribution.d.ts +19 -0
  99. package/dist/types/attribution.prod.d.ts +22 -0
  100. package/dist/types/boundaries.d.ts +99 -124
  101. package/dist/types/carved.d.ts +1 -0
  102. package/dist/types/core/action.d.ts +23 -9
  103. package/dist/types/core/async.d.ts +34 -6
  104. package/dist/types/core/attribution-costs.d.ts +35 -0
  105. package/dist/types/core/attribution-feedback.d.ts +133 -0
  106. package/dist/types/core/attribution-hooks.d.ts +307 -8
  107. package/dist/types/core/attribution-queries.d.ts +24 -0
  108. package/dist/types/core/attribution.d.ts +802 -72
  109. package/dist/types/core/constants.d.ts +195 -38
  110. package/dist/types/core/copies.d.ts +54 -0
  111. package/dist/types/core/core.d.ts +295 -65
  112. package/dist/types/core/dev.d.ts +509 -22
  113. package/dist/types/core/effect.d.ts +1 -1
  114. package/dist/types/core/error-hooks.d.ts +71 -0
  115. package/dist/types/core/error.d.ts +18 -1
  116. package/dist/types/core/graph.d.ts +40 -1
  117. package/dist/types/core/heap.d.ts +5 -5
  118. package/dist/types/core/identities.d.ts +17 -0
  119. package/dist/types/core/index.d.ts +11 -7
  120. package/dist/types/core/invariants.d.ts +1 -26
  121. package/dist/types/core/lanes.d.ts +74 -43
  122. package/dist/types/core/owner.d.ts +27 -9
  123. package/dist/types/core/scheduler.d.ts +353 -102
  124. package/dist/types/core/truth.d.ts +18 -0
  125. package/dist/types/core/types.d.ts +97 -71
  126. package/dist/types/core/verdict.d.ts +35 -0
  127. package/dist/types/deferred.d.ts +28 -0
  128. package/dist/types/flatten.d.ts +8 -0
  129. package/dist/types/from.d.ts +47 -0
  130. package/dist/types/index.d.ts +33 -7
  131. package/dist/types/map.d.ts +4 -2
  132. package/dist/types/reveal.d.ts +19 -0
  133. package/dist/types/signals.d.ts +196 -45
  134. package/dist/types/store/affects.d.ts +12 -0
  135. package/dist/types/store/edges.d.ts +9 -0
  136. package/dist/types/store/hooks.d.ts +62 -0
  137. package/dist/types/store/index.d.ts +42 -15
  138. package/dist/types/store/optimistic.d.ts +24 -45
  139. package/dist/types/store/projection.d.ts +17 -68
  140. package/dist/types/store/reconcile.d.ts +3 -46
  141. package/dist/types/store/store.d.ts +133 -136
  142. package/dist/types/store/storePath.d.ts +1 -1
  143. package/dist/types/store/target.d.ts +166 -0
  144. package/dist/types/store/types.d.ts +105 -0
  145. package/dist/types/store/utils.d.ts +188 -0
  146. package/package.json +31 -26
  147. package/dist/node.cjs +0 -8745
  148. package/dist/prod/core/optimistic.js +0 -265
  149. package/dist/prod/store/next/optimistic.js +0 -385
  150. package/dist/prod/store/next/projection.js +0 -172
  151. package/dist/prod/store/next/reconcile.js +0 -331
  152. package/dist/prod/store/next/store.js +0 -1499
  153. package/dist/prod/store/next/target.js +0 -20
  154. package/dist/types/core/optimistic.d.ts +0 -6
  155. package/dist/types/store/next/optimistic.d.ts +0 -20
  156. package/dist/types/store/next/projection.d.ts +0 -8
  157. package/dist/types/store/next/reconcile.d.ts +0 -3
  158. package/dist/types/store/next/store.d.ts +0 -77
  159. package/dist/types/store/next/target.d.ts +0 -117
  160. package/dist/types-cjs/affects.d.cts +0 -47
  161. package/dist/types-cjs/boundaries.d.cts +0 -175
  162. package/dist/types-cjs/core/action.d.cts +0 -64
  163. package/dist/types-cjs/core/async.d.cts +0 -21
  164. package/dist/types-cjs/core/attribution-hooks.d.cts +0 -52
  165. package/dist/types-cjs/core/attribution.d.cts +0 -186
  166. package/dist/types-cjs/core/constants.d.cts +0 -97
  167. package/dist/types-cjs/core/context.d.cts +0 -36
  168. package/dist/types-cjs/core/core.d.cts +0 -163
  169. package/dist/types-cjs/core/dev.d.cts +0 -81
  170. package/dist/types-cjs/core/effect.d.cts +0 -30
  171. package/dist/types-cjs/core/error.d.cts +0 -47
  172. package/dist/types-cjs/core/external.d.cts +0 -15
  173. package/dist/types-cjs/core/graph.d.cts +0 -6
  174. package/dist/types-cjs/core/heap.d.cts +0 -22
  175. package/dist/types-cjs/core/index.d.cts +0 -13
  176. package/dist/types-cjs/core/invariants.d.cts +0 -59
  177. package/dist/types-cjs/core/lanes.d.cts +0 -44
  178. package/dist/types-cjs/core/optimistic.d.cts +0 -6
  179. package/dist/types-cjs/core/owner.d.cts +0 -124
  180. package/dist/types-cjs/core/scheduler.d.cts +0 -180
  181. package/dist/types-cjs/core/types.d.cts +0 -192
  182. package/dist/types-cjs/core/verdict.d.cts +0 -2
  183. package/dist/types-cjs/index.d.cts +0 -10
  184. package/dist/types-cjs/map.d.cts +0 -69
  185. package/dist/types-cjs/package.json +0 -3
  186. package/dist/types-cjs/signals.d.cts +0 -624
  187. package/dist/types-cjs/store/index.d.cts +0 -19
  188. package/dist/types-cjs/store/next/optimistic.d.cts +0 -20
  189. package/dist/types-cjs/store/next/projection.d.cts +0 -8
  190. package/dist/types-cjs/store/next/reconcile.d.cts +0 -3
  191. package/dist/types-cjs/store/next/store.d.cts +0 -77
  192. package/dist/types-cjs/store/next/target.d.cts +0 -117
  193. package/dist/types-cjs/store/optimistic.d.cts +0 -45
  194. package/dist/types-cjs/store/projection.d.cts +0 -70
  195. package/dist/types-cjs/store/reconcile.d.cts +0 -46
  196. package/dist/types-cjs/store/store.d.cts +0 -138
  197. package/dist/types-cjs/store/storePath.d.cts +0 -58
  198. package/dist/types-cjs/store/utils.d.cts +0 -74
@@ -1,476 +1,465 @@
1
- import { NOT_PENDING, REACTIVE_DISPOSED, REACTIVE_DIRTY, REACTIVE_CHECK, unwrapOverride, REACTIVE_ZOMBIE, STATUS_UNINITIALIZED, STATUS_PENDING, REACTIVE_OPTIMISTIC_DIRTY, REACTIVE_MANUAL_WRITE, CONFIG_HAS_COMPANIONS, CONFIG_CHILD_COMPANIONS, STATUS_ERROR, REACTIVE_RECOMPUTING_DEPS } from "./constants.js";
1
+ import { REACTIVE_PROBED, REACTIVE_WINDOW, CONFIG_VERDICT, CONFIG_ASYNC, CONFIG_CHILDREN_FORBIDDEN, CONFIG_OVERRIDE, STATUS_ERROR, CONFIG_GUESS, STATUS_PENDING, NOT_PENDING, STATUS_UNINITIALIZED, CONFIG_HELD, EFFECT_RENDER, CONFIG_STAGED_ERROR, REACTIVE_STAGED_READ, REACTIVE_PROBE_UNANSWERED, REACTIVE_REASK, CONFIG_INPUTS_PUBLISHED, REACTIVE_JOINED, REACTIVE_LANE_READ } from "./constants.js";
2
2
 
3
- import { setSignal, prepareComputed, read, context, stale, currentOptimisticLane, tracking, ext, setLatestReadActive, setContextInternal, optimisticComputed, setPendingCheckActive, latestReadActive, optimisticSignal, pendingCheckActive } from "./core.js";
3
+ import { context, creatorPass, tracking, creatorReads, setVerdict, verdict, prepareComputed, pullComputed, joinPass, markLateLinker, spectating, unflushedValue, bornInto } from "./core.js";
4
4
 
5
5
  import { NotReadyError } from "./error.js";
6
6
 
7
7
  import { link } from "./graph.js";
8
8
 
9
- import { insertIntoHeap, queueFor, markHeap, enqueueSub } from "./heap.js";
10
-
11
- import "./invariants.js";
12
-
13
- import { findLane, hasActiveOverride, assignOrMergeLane } from "./lanes.js";
14
-
15
- import { installOptimisticEngine } from "./optimistic.js";
16
-
17
- import { GlobalQueue, insertSubs, schedule, activeTransition, currentTransition, activeAffectsMarks } from "./scheduler.js";
18
-
19
- /**
20
- * The isPending()/latest() verdict layer, moved out of core.ts. Importing this
21
- * module installs the companion-maintenance hooks on GlobalQueue; apps that
22
- * never import isPending/latest never pay for any of it.
23
- */
24
- // Companions (pending signals / latest shadows) are optimistic nodes: their
25
- // writes go through the optimistic write path and their reversion rides the
26
- // same lanes, so the verdict layer brings the engine with it.
27
- installOptimisticEngine();
28
-
29
- let pendingProbe = null;
30
-
31
- /**
32
- * Probes whose verdict was suppressed by the fresh-read pairing rule while
33
- * the held write's fate was still undecided (see recordFreshRead /
34
- * wakeSuppressedProbes): held node → the wrapper computeds that probed it.
35
- * Entries die with the hold — the commit/revert snap clears them.
36
- */ const suppressedProbes = new Map;
37
-
38
- /**
39
- * Get or create the pending signal for a node (lazy).
40
- * Used by isPending() to track pending state reactively.
41
- */
42
- /** #3038: register a companion-carrying firewall child on its firewall's
43
- * companion set and arm the post-recompute snap (CONFIG_CHILD_COMPANIONS is
44
- * the one-load gate at the call sites). The snap then iterates exactly the
45
- * children someone asked verdicts of — O(companions) — never the full
46
- * `_child` chain, which carries one node per materialized leaf (the
47
- * O(all-leaves-ever-read)-per-update pathology). Entries are permanent like
48
- * the companions themselves; a store with no leaf-level isPending()/latest()
49
- * reads never allocates the set or pays the walk. */ function markFirewallChildCompanions(e) {
50
- const t = e.lt;
51
- if (!t) return;
52
- t.T |= CONFIG_CHILD_COMPANIONS;
53
- (ext(t).Nt ??= new Set).add(e);
9
+ import { display, verdictLane } from "./lanes.js";
10
+
11
+ import { installVerdictHooks, GlobalQueue, flushTransaction, txOf, passLane, staleReader, globalQueue, joinFuture, stagedReaders, queuePendingNode, setPassLane } from "./scheduler.js";
12
+
13
+ import { laneValueOf } from "./truth.js";
14
+
15
+ // Verdicts (plan sec. 28 replay, 2026-10-02; maintainer, 2026-10-01: verdicts are
16
+ // optimistic state the system supplies). `isPending(fn)` asks whether what
17
+ // `fn` reads is final; `latest(fn)` reads the proposal — the staged write or
18
+ // landing, the lane's value — instead of the screen. A verdict is a guess
19
+ // ("pending, until this lands"; "the proposal is the value") and its readers
20
+ // break out of the hold the way a lane's do: work of the holder's verdict
21
+ // lane (lanes.ts), shown now, re-derived at the landing, ending with it. A
22
+ // verdict reader is a frame reader: its plain reads of held nodes see the
23
+ // screen (core.ts `frameRead`), so `[isPending(x), x()]` never pairs the
24
+ // fresh value with pending (A10).
25
+ /** A verdict reader whose answer this flush could not give yet: the node it
26
+ * asked about is staged and not held — whether it will be is the seam's
27
+ * verdict. It read the staging as the screen: if the flush parks it runs
28
+ * again next round, as lane work that did the same does (`stagedReaders`,
29
+ * lanes.ts — entering the verdict lane then voids what it staged); if it
30
+ * commits, the answer stood. */
31
+ function watchVerdict(e, t = false) {
32
+ if (t) e.R |= REACTIVE_PROBE_UNANSWERED;
33
+ stagedReaders.push(e);
54
34
  }
55
35
 
56
- function getPendingSignal(e) {
57
- let t = e.o?.Ge;
58
- if (!t) {
59
- // Start false, write true if pending - ensures reversion returns to false
60
- t = optimisticSignal(false, {
61
- ownedWrite: true
62
- });
63
- ext(e).Ge = t;
64
- e.T |= CONFIG_HAS_COMPANIONS;
65
- markFirewallChildCompanions(e);
66
- ext(t).It = e;
67
- if (computePendingState(e)) setSignal(t, true);
68
- }
69
- return t;
70
- }
71
-
72
- function collectPendingSources(e) {
73
- if (!pendingProbe) return;
74
- pendingProbe.sources.add(e);
75
- const t = e.lt || e;
76
- if (t !== e) pendingProbe.sources.add(t);
77
- }
78
-
79
- /**
80
- * Adds a node to the active isPending() probe without reading it. The store's
81
- * untracked-probe fallback (`witnessAffectsMark`) reaches this through
82
- * `GlobalQueue._witnessAffects` — its callers guard on `pendingCheckActive`,
83
- * which only flips inside `isPending()`, so the hook is always installed by
84
- * the time it can fire.
85
- */ function witnessAffects(e) {
86
- pendingProbe?.sources.add(e);
36
+ /** The reading pass is the holder's verdict lane's — unless already lane
37
+ * work, or already the transaction's (it derives from the future — a held
38
+ * node's, or the staging of a write this flush may hold — and the verdict
39
+ * does not rescue it: its result shows at the landing, re-derived). A lane
40
+ * read, for `recompute`'s tail (REACTIVE_LANE_READ). */ function route(e, t) {
41
+ if (!passLane && !(e.R & (REACTIVE_JOINED | REACTIVE_STAGED_READ))) setPassLane(verdictLane(t));
42
+ e.R |= REACTIVE_LANE_READ;
87
43
  }
88
44
 
89
- /**
90
- * The affects() coverage walk — the read half of the dedicated mark channel.
91
- * A node is covered by a live mark iff it carries one (`_affectsCount`) or
92
- * derives, through its CURRENT deps (hopping store firewalls), from a node
93
- * that does. Pull-based coverage means graph rewires, mid-window recomputes,
94
- * and probe-triggered recomputes can never strand or strip a mark — there is
95
- * nothing stored downstream to corrupt. Probe-created links
96
- * (`_pendingObserver`) are skipped so an `isPending` wrapper memo never
97
- * inherits the coverage it reports on.
98
- */ function markWalk(e, t) {
99
- if (e.o?.t) return true;
100
- // A real error outranks an inherited mark (A16/A24c): an errored node
101
- // answers probes with its error, not a coverage verdict, and coverage does
102
- // not flow through it — matching the rails' behavior, where propagation
103
- // stopped at errored nodes. A DIRECT mark on an errored node still reads
104
- // pending (the count check above), also matching.
105
- if (e.S & STATUS_ERROR) return false;
106
- if (t.has(e)) return false;
107
- t.add(e);
108
- const n = e.lt;
109
- if (n && markWalk(n, t)) return true;
110
- // Mid-recompute (the clearStatus companion poke runs before
111
- // trimStaleDeps), only the validated prefix [_deps.._depsTail] is this
112
- // pass's dependency set — walking past it would read dropped deps and
113
- // latch a stale verdict on the companion.
114
- const i = e;
115
- const r = i.ie & REACTIVE_RECOMPUTING_DEPS ? i.Ye : undefined;
116
- if (r !== null) {
117
- for (let e = i.nt ?? null; e !== null; e = e.it) {
118
- if (!e.me && markWalk(e.ut, t)) return true;
119
- if (e === r) break;
120
- }
45
+ /** A verdict reader served a flight's committed value (the A31 frame-reader
46
+ * case) still observed the flight — A15's stale reader "derives from the
47
+ * flight all the same": the frame holds (the flush's transaction opens if
48
+ * none has, as the throw would have had it), the reader is that
49
+ * transaction's verdict lane's (shown now, with the flight's verdict), and
50
+ * re-derives at the landing (REACTIVE_FRAME_READ — which is also what makes
51
+ * it a blocker of the hold, `blockedBy`; a probe alone is neither). The
52
+ * hold is on the flight: a reader served committed never goes pending, so
53
+ * the transaction waits on it only through the flight's node — one that
54
+ * went pending this flush is queued for the seam to hold; one in the air
55
+ * from before, held by nobody (a lane's derivation whose lane dissolved at
56
+ * its parent's landing with the flight still up; a flight nothing displayed,
57
+ * #3305), is registered by this, its first observer (A15, #3458): queued for
58
+ * the seam as if it had gone pending here, so the transaction has an entry
59
+ * for what it now shows a reader of. Held by nothing, it landed at this very
60
+ * seam and its `_reruns` re-derived the reader into the same observation,
61
+ * every flush (fuzzer F12). True when the reader is served the committed
62
+ * value — not over a flight committed beneath inputs already on screen (A15
63
+ * reveal corollary, #3305): that value would tear against them, and the
64
+ * reveal observes the flight. Nor for a reader born into the flight's
65
+ * transaction (`bornInto`, #4010): it has no screen — it waits on the flight
66
+ * with the rest of its frame. */ function observeFlight(e, t) {
67
+ if (t.l & CONFIG_INPUTS_PUBLISHED || t.l & CONFIG_HELD && bornInto(e, txOf(t))) return false;
68
+ if (globalQueue.Y) {
69
+ joinFuture(null);
70
+ if (!(t.l & CONFIG_HELD)) queuePendingNode(t);
71
+ verdictRead(e, flushTransaction, true);
121
72
  }
122
- return false;
73
+ return true;
123
74
  }
124
75
 
125
- /** Gated entry: apps with no live mark pay one integer compare. */ function markCovered(e) {
126
- return activeAffectsMarks !== 0 && markWalk(e, new Set);
76
+ /** Verdict windows. Inside `latest(fn)` a read of a held node serves the
77
+ * proposal (above the async, the held write; below it, the committed value
78
+ * — nothing newer exists) instead of joining or throwing; inside
79
+ * `isPending(fn)` a read records whether what it saw is final. Either way
80
+ * the reader breaks out: work of the holder's verdict lane, shown now and
81
+ * re-derived at the landing. */ let latestActive = false;
82
+
83
+ let probing = false;
84
+
85
+ let probeFound = false;
86
+
87
+ /** A verdict read of a node a transaction (or lane) holds: the reading pass
88
+ * is the holder's verdict lane's and re-derives at the holder's landing
89
+ * (`_reruns`, as a stale reader). `isPending` flips at the landing with no
90
+ * value notifying anyone (a held write's commit; a held landing's): its
91
+ * reader re-derives there. `latest` shows the proposal that lands — a
92
+ * rewrite or a landing reaches its reader through the link it holds — so a
93
+ * re-run would only repeat the frame. */ function verdictRead(e, t, r) {
94
+ route(e, t);
95
+ if (r) staleReader(e, t); else if (probing && t.En[t.En.length - 1] !== e) t.En.push(e);
127
96
  }
128
97
 
129
- function quietPending(e) {
130
- if (e.o?.le) {
131
- for (const t of e.o.le) if (!t.o?.pe) return false;
132
- return true;
98
+ /** A verdict read of a node staged or in flight but not held yet: whether it
99
+ * will be is the seam's.
100
+ *
101
+ * `isPending` asks exactly that — final unless the flush parks — so its
102
+ * reader cannot be answered before the seam: a flush that has joined a
103
+ * transaction parks, and the node with it — the reader is that
104
+ * transaction's verdict lane's now; before anything joined, the reader
105
+ * watches the seam and runs again if the flush parks (a plain sync write
106
+ * never glitches it true).
107
+ *
108
+ * `latest` has its answer either way — the proposal — but its reader's
109
+ * membership is the seam's too: the frame opens no transaction for it
110
+ * (#4365 — one opened for the read alone made a plain read after the window
111
+ * take it for a hold, `stagedScreen`, and serve the screen beside the
112
+ * staging in a frame nothing held). The reader watches the seam like a
113
+ * probe before anything joined: its pass is the frame's, and runs again in
114
+ * the verdict lane if the flush parks. Once the flush has joined a
115
+ * transaction, the reader is its verdict lane's at once; a pass that also
116
+ * read the frame's own stagings plainly (REACTIVE_STAGED_READ) stays the
117
+ * frame's (`route` does not move it). */ function provisionalVerdict(e, t) {
118
+ if (flushTransaction) {
119
+ if (probing && t) probeFound = true;
120
+ verdictRead(e, flushTransaction);
121
+ if (probing && e.R & REACTIVE_STAGED_READ) watchVerdict(e);
122
+ return;
133
123
  }
134
- return e.o?.pe ?? false;
124
+ // Before anything joined, a staging that would not be final held has no
125
+ // answer yet: whatever the seam decides, the pass runs again — a later
126
+ // plain read of the same pass may route it into the verdict lane
127
+ // (`observeFlight`), which the seam must not take as this probe's answer.
128
+ // `latest` opens no transaction for its answer either (#4365): the plain
129
+ // read of a staging after it took one opened for it for a hold and paired
130
+ // the staging with the screen, in a frame nothing held. Its pass is the
131
+ // frame's — whole — and re-derives once if the flush parks (unanswered:
132
+ // its membership is the seam's, not the transaction it joins).
133
+ watchVerdict(e, !probing || t);
135
134
  }
136
135
 
137
- // NOTE: a loadingValue node's open loading window (_loading) is verdict-quiet
138
- // on purpose: commit #0 answers the question by declaration, so the window
139
- // reads NOT pending — first-load affordances live in the value channel
140
- // (null / skeleton provenance the author encoded), and isPending stays what
141
- // it always was: refetch truth for an answered question. This keeps the
142
- // verdict fully correlated with transition-class machinery and keeps server
143
- // (always false) and client hydration trivially consistent.
144
- function newQuestionInFlight(e) {
145
- return !!(e.S & STATUS_PENDING) && !(e.S & STATUS_UNINITIALIZED) && !quietPending(e);
146
- }
147
-
148
- function computePendingState(e) {
149
- const t = e;
150
- if (t.ie & REACTIVE_DISPOSED) return false;
151
- // Mark coverage is transitive by dep-graph reachability: a latest() shadow
152
- // reaches its owner (and a store leaf its firewall) through its own deps,
153
- // so the one walk covers direct marks, derivation, and companion chains.
154
- if (markCovered(e)) return true;
155
- const n = e.lt;
156
- if (e.o?.It) {
157
- const t = e.o?.It;
158
- const n = t.lt || t;
159
- return newQuestionInFlight(n);
160
- }
161
- if (n && e.Pe !== NOT_PENDING && !hasActiveOverride(e)) {
162
- return !!(n.ie & REACTIVE_MANUAL_WRITE) || !n.o?.Ee && !(n.S & STATUS_PENDING) || !!(n.S & STATUS_PENDING) && quietPending(n);
163
- }
164
- // `!comp._loading`: a hold created while the loading window is still open is
165
- // the window's own landing in flight to its commit — verdict-quiet like the
166
- // rest of the window (the UNINITIALIZED check suppresses exactly this frame
167
- // for windowless first loads; born-committed nodes need their own gate, #2990).
168
- if (e.Pe !== NOT_PENDING && !(t.S & STATUS_UNINITIALIZED) && !t.Ie) {
169
- if (hasActiveOverride(e)) return !e.Ue || !e.Ue(e.Pe, unwrapOverride(e.o?.De));
136
+ /** A19 exc. 2 / A24: a flight that re-asks the question already answered
137
+ * (a `refresh()`, a poll) is quiet; a node pending through others is quiet
138
+ * when every flight it waits on is. */ function quietPending(e) {
139
+ const t = e.T;
140
+ if (!t) return false;
141
+ if (t.se) {
142
+ for (const e of t.se) if (!e.T?.Ve) return false;
170
143
  return true;
171
144
  }
172
- return newQuestionInFlight(t);
173
- }
174
-
175
- function syncCompanions(e, t) {
176
- if (e.o?.Ge) updatePendingSignal(e);
177
- if (e.o?.ge) setSignal(e.o?.ge, t);
178
- }
179
-
180
- function updatePendingSignal(e) {
181
- if (e.o?.Ge) {
182
- setSignal(e.o?.Ge, computePendingState(e));
183
- }
184
- if (e.o?.ge) updatePendingSignal(e.o?.ge);
185
- }
186
-
187
- function updateChildCompanions(e) {
188
- const t = e.o?.Nt;
189
- if (t === undefined) return;
190
- for (const e of t) updatePendingSignal(e);
145
+ return t.Ve;
191
146
  }
192
147
 
193
- /**
194
- * Re-derive every verdict companion downstream of `el` (subs + firewall
195
- * children, dedup'd). The affects() channel's poke walk: registration and
196
- * re-ask flips use the live write path (companion setSignal — its own lane
197
- * lets the wake escape an incomplete transition's effect stash, #2887);
198
- * mark release passes `snap` because it runs inside queue finalization,
199
- * where companion writes must land committed (a setSignal there would open
200
- * a fresh override window that nothing settles).
201
- */ function repollDownstreamVerdicts(e, t = false) {
202
- const n = t ? snapCompanionsToState : updatePendingSignal;
203
- const i = new Set;
204
- const visit = e => {
205
- if (i.has(e)) return;
206
- i.add(e);
207
- if (e.o?.Ge || e.o?.ge) n(e);
208
- for (let t = e.u; t !== null; t = t.fe) visit(t.ae);
209
- for (let t = e.o?.l ?? null; t !== null; t = t.ce) {
210
- visit(t);
211
- }
212
- };
213
- visit(e);
214
- }
215
-
216
- /**
217
- * The correction half of the provisional fresh-read suppression (see
218
- * collectPending): fired from the sanctioned async-registration site
219
- * (GlobalQueue.notify) when a transaction gains an in-flight async blocker.
220
- * Every probe that returned "not pending" purely because it read a held
221
- * value belonging to that transaction re-runs — its re-probe now sees the
222
- * live blocker through heldAwaitingAsync and lands the true verdict. The
223
- * wake mirrors a companion write's own notification (optimistic-dirty on the
224
- * companion's lane) so the corrected verdict commits and flushes immediately
225
- * instead of being held with the transaction it reports on.
226
- */ function wakeSuppressedProbes(e) {
227
- if (suppressedProbes.size === 0) return;
228
- let t = false;
229
- for (const [n, i] of suppressedProbes) {
230
- const r = n._e;
231
- const s = r ? currentTransition(r) : null;
232
- if (!s) {
233
- suppressedProbes.delete(n);
234
- continue;
235
- }
236
- if (s !== e) continue;
237
- suppressedProbes.delete(n);
238
- const o = n.o?.Ge?.o?.Be;
239
- for (const e of i) {
240
- if (e.ie & REACTIVE_DISPOSED) continue;
241
- e.ie |= REACTIVE_OPTIMISTIC_DIRTY;
242
- if (o) assignOrMergeLane(e, o); else if (e.o !== null) e.o.Be = undefined;
243
- enqueueSub(e);
244
- t = true;
245
- }
246
- }
247
- if (t) schedule();
148
+ /** `isPending`'s answer for a held node (A19), relative to what the reader
149
+ * sees — a verdict reader is a frame reader and sees the committed value: a
150
+ * flight still up (cause ii) is not final unless quiet; a held write or
151
+ * landing (staged — cause i/iii) is not final unless it answers the question
152
+ * already shown (a quiet re-ask, through its reveal), is the node's first
153
+ * landing (commit #0: loading, not pending), or the probe sits inside a
154
+ * `latest` window, whose view of the node IS the proposal (A10: a reader
155
+ * that observed the fresh value is not told it is pending; inside `latest`,
156
+ * `isPending` asks whether the latest view is final). A member of a held
157
+ * frame with neither is final. A24: pending iff a value change is in
158
+ * flight — a staging equal to what is displayed changes nothing when it
159
+ * lands: final now (maintainer, 2026-10-02). */ function heldNotFinal(e) {
160
+ if ((e.m & STATUS_PENDING) !== 0 && !quietPending(e)) return true;
161
+ return e.ue !== NOT_PENDING && !latestActive && !e.T.Ve && !(e.m & STATUS_UNINITIALIZED) && (!!e.T.Te || !e.ye || !e.ye(e.le, e.ue));
248
162
  }
249
163
 
250
- function snapCompanionsToState(e) {
251
- suppressedProbes.size !== 0 && suppressedProbes.delete(e);
252
- const t = e.o?.Ge;
253
- if (t && (t.o?.De === undefined || t.o?.De === NOT_PENDING)) {
254
- const n = computePendingState(e);
255
- if (t.be !== n || t.Pe !== NOT_PENDING) {
256
- t.be = n;
257
- t.Pe = NOT_PENDING;
258
- insertSubs(t);
259
- schedule();
260
- }
261
- }
262
- const n = e.o?.ge;
263
- if (n && !(n.ie & REACTIVE_DISPOSED)) {
264
- if ((n.o?.De === undefined || n.o?.De === NOT_PENDING) && n.Pe === NOT_PENDING && !Object.is(n.be, e.be) && !(n.ie & (REACTIVE_DIRTY | REACTIVE_CHECK))) {
265
- n.ie |= REACTIVE_DIRTY;
266
- insertIntoHeap(n, queueFor(n));
267
- insertSubs(n);
268
- schedule();
269
- }
270
- snapCompanionsToState(n);
164
+ /** `isPending`'s answer for an unheld node whose flight is up (cause ii). */ function pendingVerdict(e, t) {
165
+ if (t.m & STATUS_UNINITIALIZED) return;
166
+ if (probing && !quietPending(t)) probeFound = true;
167
+ if (e && tracking) {
168
+ if (t.l & CONFIG_HELD) verdictRead(e, txOf(t)); else if (globalQueue.Y) {
169
+ // `latest` of a flight serves the committed value, the frame's and the
170
+ // lane's alike (D-22): nothing is the seam's, so the reader is the
171
+ // verdict lane's now, as a plain read of a flight makes it
172
+ // (`observeFlight`). Its frame pass re-derived in the lane (R5) asked
173
+ // its question again (F-9, #4397). A staging's is the seam's (#4365).
174
+ if (!probing) joinFuture(null);
175
+ provisionalVerdict(e, false);
176
+ // decided above
177
+ }
271
178
  }
272
179
  }
273
180
 
274
- function getLatestValueComputed(e) {
275
- let t = e.o?.ge;
276
- if (!t) {
277
- const n = latestReadActive;
278
- setLatestReadActive(false);
279
- const i = pendingCheckActive;
280
- setPendingCheckActive(false);
281
- const r = context;
282
- setContextInternal(null);
283
- // Detach from owner so it isn't disposed with effects
284
- t = optimisticComputed(() => read(e));
285
- ext(e).ge = t;
286
- e.T |= CONFIG_HAS_COMPANIONS;
287
- markFirewallChildCompanions(e);
288
- ext(t).It = e;
289
- // Parent-child lane relationship
290
- // Backfill an in-flight write (mirrors getPendingSignal): the companion is
291
- // created lazily, possibly after the write was processed — syncCompanions
292
- // only pushes into companions that already exist, so the first latest()
293
- // read inside a held transition showed the committed value (#3041).
294
- if (e.Pe !== NOT_PENDING && !hasActiveOverride(e)) setSignal(t, e.Pe);
295
- setContextInternal(r);
296
- setPendingCheckActive(i);
297
- setLatestReadActive(n);
181
+ /** The pass entering a verdict window is a verdict reader (CONFIG_VERDICT):
182
+ * a dependency going pending re-derives it, the verdict having changed. Not
183
+ * an untracked window: that is a snapshot, like any untracked read (#3946 —
184
+ * a router's link claim probing inside the computation that creates the
185
+ * anchor). A throw still reaches the pass through `untrack`. */ function markVerdictReader(e) {
186
+ const t = creatorPass(context);
187
+ if (t && tracking) {
188
+ // A pass run inside passes still running — its creator's, theirs — one
189
+ // of which read the future or a staging (a branch a held or staged
190
+ // condition opened, and whatever that branch creates) has no screen: the
191
+ // branch exists only in that world. It reads that world as any
192
+ // derivation of it does (`bornInto` once held) and stays its pass
193
+ // (`route`); a verdict reader from its next pass. #4341: a memo born in
194
+ // a newly opened <Match> read the screen, where its branch was closed.
195
+ // Not such a pass: a verdict reader now.
196
+ t.l &= ~CONFIG_ASYNC;
197
+ t.R |= REACTIVE_PROBED | REACTIVE_WINDOW | (creatorReads(t, 0) || (t.l |= CONFIG_VERDICT,
198
+ 0));
298
199
  }
299
- return t;
300
200
  }
301
201
 
302
- /** The latest()-mode read path, installed as GlobalQueue._latestRead. */ function latestRead(e) {
303
- const t = getLatestValueComputed(e);
304
- const n = latestReadActive;
305
- setLatestReadActive(false);
306
- const i = e.o?.De !== undefined && e.o?.De !== NOT_PENDING ? unwrapOverride(e.o?.De) : e.be;
307
- let r;
202
+ /** The windows. `read` dispatches to `verdictValue` while one is open; each
203
+ * restores the dispatch it found — none inside a memo's pass a window
204
+ * pulled (`verdictValue`). */ function latest(e) {
205
+ markVerdictReader();
206
+ const t = latestActive, r = verdict;
207
+ latestActive = true;
208
+ setVerdict(verdictValue);
308
209
  try {
309
- // An untracked latest() read has no reading context, so read() never
310
- // performs its mid-tick pull — a plain write queued between two latest()
311
- // calls left a still-subscribed shadow at its previous speculative value
312
- // until the flush (#2922). Mirror the tracked-read pull here: mark the
313
- // queued staleness through the graph, then bring the shadow up to date.
314
- const e = queueFor(t);
315
- if (t.Le >= e.xe && !(t.ie & (REACTIVE_DISPOSED | REACTIVE_ZOMBIE))) {
316
- markHeap(e);
317
- prepareComputed(t, true);
318
- }
319
- r = read(t);
320
- } catch (t) {
321
- if (t instanceof NotReadyError && (!context || !(e.S & STATUS_UNINITIALIZED))) return i;
322
- throw t;
210
+ return e();
323
211
  } finally {
324
- setLatestReadActive(n);
325
- }
326
- if (t.S & STATUS_PENDING) return i;
327
- if (stale && currentOptimisticLane && t.o?.Be) {
328
- const e = findLane(t.o?.Be);
329
- const n = findLane(currentOptimisticLane);
330
- if (e !== n && e.Ae.size > 0) {
331
- return i;
332
- }
212
+ latestActive = t;
213
+ setVerdict(r);
333
214
  }
334
- // A shadow recomputed by the pull above (not at creation) holds its fresh
335
- // speculative value in _pendingValue; a contextless read() only surfaces
336
- // _value. Overrides stay authoritative (A17), and stale readers keep the
337
- // other transition's committed view, matching read()'s own selection.
338
- if (t.Pe !== NOT_PENDING && !hasActiveOverride(t) && !(stale && t._e && activeTransition !== t._e)) return t.Pe;
339
- return r;
340
- }
341
-
342
- /** The isPending()-probe read path, installed as GlobalQueue._pendingCheck. */ function pendingCheckRead(e, t, n, i) {
343
- setPendingCheckActive(false);
344
- if (typeof e.Se === "function") prepareComputed(e, true);
345
- const r = n.S;
346
- if (t && r & STATUS_PENDING && r & STATUS_UNINITIALIZED) {
347
- if (tracking && e !== t) link(e, t);
348
- setPendingCheckActive(true);
349
- throw n.o?._;
350
- }
351
- collectPendingSources(e);
352
- if (i) collectPendingSources(i);
353
- setPendingCheckActive(true);
354
215
  }
355
216
 
356
217
  /**
357
- * A held node whose transaction still has an async question in flight. The
358
- * probe's fresh-read pairing rule (#2831 — "a reader that sees the fresh
359
- * value must not also be told it is pending") only applies to LANDED answers
360
- * awaiting reveal; while the answer is still computing, the fresh value the
361
- * reader saw is an input, and pending remains the truth for every reader
362
- * (#3028).
363
- */ function heldAwaitingAsync(e) {
364
- const t = e._e;
365
- const n = t ? currentTransition(t) : null;
366
- if (!n || n.an) return false;
367
- for (const [e, t] of n.Ne) {
368
- if (t.size && e.S & STATUS_PENDING && e.o?._?.source === e) return true;
218
+ * Reports whether what `fn` reads is still waiting on an update. Read the
219
+ * values in question inside `fn`: the probe answers `true` while any of
220
+ * them depends on a change that is in flight — a write or an async source
221
+ * that has started producing a new value whose result has not landed yet —
222
+ * and `false` once the latest value is the one on screen. The answer is
223
+ * tracked like any read, so a memo or effect that calls `isPending` re-runs
224
+ * when it flips.
225
+ *
226
+ * A read that has no value yet is loading, not pending: inside `fn` it
227
+ * follows the surrounding `Loading` path like any other read, and the probe
228
+ * reports only updates to a value that already exists. A bare `refresh()`
229
+ * does not flip `isPending`; pair it with `affects()` to make the re-ask
230
+ * visible.
231
+ *
232
+ * @param fn expression to probe; read the values in question inside it
233
+ *
234
+ * @returns `true` while a read inside `fn` depends on a change that has not committed yet
235
+ *
236
+ * @example
237
+ * ```ts
238
+ * const [query, setQuery] = createSignal("");
239
+ * const results = createMemo(() => searchProducts(query()));
240
+ * const stale = createMemo(() => isPending(results));
241
+ *
242
+ * setQuery("shoes");
243
+ * // stale() is true until the new results land, while results() keeps
244
+ * // serving the previous list — dim it instead of showing a spinner.
245
+ * ```
246
+ *
247
+ * @description https://v2.solidjs.com/reference/solid-js/reactivity/is-pending
248
+ */ function isPending(e) {
249
+ markVerdictReader();
250
+ const t = probing;
251
+ const r = probeFound;
252
+ const i = verdict;
253
+ probing = true;
254
+ probeFound = false;
255
+ setVerdict(verdictValue);
256
+ try {
257
+ e();
258
+ return probeFound;
259
+ } catch (e) {
260
+ // A pending node is served inside the probe; what still throws is an
261
+ // uninitialized one — loading, not pending (A19 exc. 1) — whose NotReady
262
+ // propagates to boundaries from inside an owner (B5a; a component body
263
+ // suspends on it like any read, #2928) and is swallowed outside one. A
264
+ // verdict never throws anything else: a thunk that errors is simply not
265
+ // pending.
266
+ if (e instanceof NotReadyError && !probeFound && context !== null) throw e;
267
+ return probeFound;
268
+ } finally {
269
+ probing = t;
270
+ probeFound = r;
271
+ setVerdict(i);
369
272
  }
370
- return false;
371
273
  }
372
274
 
373
- function recordFreshRead(e, t) {
374
- if (pendingProbe !== null && e.Pe !== NOT_PENDING && t === e.Pe) {
375
- if (heldAwaitingAsync(e)) return;
376
- pendingProbe.freshReads.add(e);
377
- }
275
+ /** The staging, or the staged error thrown (D-41). */ function staged(e) {
276
+ if (e.l & CONFIG_STAGED_ERROR) throw e.ue;
277
+ return e.ue;
378
278
  }
379
279
 
380
- function applyReask(e, t) {
381
- const n = !!(e.S & STATUS_PENDING);
382
- const i = t && !(n && !e.o?.pe);
383
- const r = n && (e.o?.pe ?? false) !== i;
384
- // Allocation-free for the quiet case: false is the extension default.
385
- if (i) ext(e).pe = true; else if (e.o !== null) e.o.pe = false;
280
+ /** `latest()` of a held node: the flushed staging — the proposal — and, for
281
+ * a tracked reader of a rewrite not yet flushed, the late-linker re-run in
282
+ * the flush that carries it. */ function heldLatest(e, t) {
283
+ const r = unflushedValue(e);
284
+ if (r === NOT_PENDING) return staged(e);
285
+ if (t) markLateLinker(t);
386
286
  return r;
387
287
  }
388
288
 
389
- function latest(e) {
390
- const t = latestReadActive;
391
- setLatestReadActive(true);
392
- try {
393
- return e();
394
- } finally {
395
- setLatestReadActive(t);
289
+ /** The verdict windows' read — `latest(fn)` and `isPending(fn)` — in one
290
+ * cold path off `read`'s. It links and pulls like a plain read, then answers
291
+ * for what the reader sees (a verdict reader is a frame reader):
292
+ * - a lane's node: the reader is the holder's verdict lane's (not the
293
+ * lane's — a lane blocked on its own flight would hold the reader's run,
294
+ * the opposite of a verdict's display-ahead). A guess is the value (A17),
295
+ * not final while its own question flies or a truth held beneath it
296
+ * differs (A24, A18 provenance). A derivation: `latest` sees the lane's
297
+ * latest (a landing a blocked lane holds has answered its question — the
298
+ * checkout's shipping text updates independently of the tax in flight),
299
+ * `isPending` the screen; not final while its own flight is up. A
300
+ * derivation's first load in flight (nothing produced yet — an async memo
301
+ * over a verdict memo, F-6) reads as the flight below: loading (D-22);
302
+ * - a held node: the reader breaks out as the holder's verdict lane's and
303
+ * re-derives at the landing; `latest` serves the proposal (`heldLatest`),
304
+ * `isPending` answers `heldNotFinal`;
305
+ * - a node staged this flush and not yet held: the seam's verdict
306
+ * (`provisionalVerdict`) — or, with a newer question already in flight
307
+ * (#3376, the stale first hop of a chain), its flight's;
308
+ * - a flight (cause ii): `isPending` records it (`pendingVerdict`), `latest`
309
+ * serves the committed value — below the async nothing newer exists; an
310
+ * uninitialized one throws NotReady: loading, not pending (A19 exc. 1);
311
+ * - an errored derivation throws for every reader (#2897). */ function verdictValue(e, t) {
312
+ const r = e;
313
+ const i = !!t && !(t.l & CONFIG_CHILDREN_FORBIDDEN);
314
+ const n = i && tracking;
315
+ // A31: the pulled memo's pass is its own, not the probe's — it reads
316
+ // outside the window, and the probe's read below answers for what the pass
317
+ // produced. So is a lazy or dormant memo's wake (an unowned memo with no
318
+ // reader left, #4366): woken inside the window, it read its sources as the
319
+ // probe does, the committed value under a landing. (A signal has no
320
+ // `_flags`: the wake is a no-op for it.)
321
+ setVerdict(null);
322
+ prepareComputed(r);
323
+ if (t && tracking) {
324
+ link(e, t);
325
+ if (e.X) pullComputed(r, t);
396
326
  }
397
- }
398
-
399
- function isPending(e) {
400
- const t = pendingCheckActive;
401
- const n = pendingProbe;
402
- setPendingCheckActive(true);
403
- const i = pendingProbe = {
404
- found: false,
405
- sources: new Set,
406
- freshReads: new Set,
407
- suppressed: []
408
- };
409
- const collectPending = () => {
410
- setPendingCheckActive(false);
411
- try {
412
- i.sources.forEach(e => {
413
- if (read(getPendingSignal(e))) {
414
- if (!i.freshReads.has(e)) i.found = true; else i.suppressed.push(e);
415
- }
416
- });
417
- } finally {
418
- setPendingCheckActive(true);
327
+ setVerdict(verdictValue);
328
+ // A live `affects()` mark on the node or a dependency (affects.ts): not
329
+ // final by declaration — the value itself is read as below. The verdict is
330
+ // display-ahead like any: in a transaction's flush (the declaring action's
331
+ // body) the reader is its verdict lane's, not a staging the frame parks.
332
+ if (probing && GlobalQueue.p?.(r)) {
333
+ probeFound = true;
334
+ if (n && flushTransaction) verdictRead(t, flushTransaction);
335
+ }
336
+ if (e.l & CONFIG_OVERRIDE && !(r.m & STATUS_ERROR && !(e.l & CONFIG_GUESS))) {
337
+ const i = txOf(e), o = (r.m & STATUS_PENDING) !== 0 && !quietPending(r), a = e.l & CONFIG_GUESS,
338
+ // NOT_PENDING: a node born in the lane with nothing in it yet has no
339
+ // value in either view — loading, not pending, and no verdict to read
340
+ // ahead: it is read as any uninitialized node, NotReady below (A7, A19
341
+ // exc. 1; #4363), its reader not moved into the verdict lane.
342
+ u = a ? 0 : latestActive && !o ? laneValueOf(e) : display(e);
343
+ if (u !== NOT_PENDING) {
344
+ if (n) verdictRead(t, i._parent ?? i);
345
+ if (a) {
346
+ if (probing && (o || e.ue !== NOT_PENDING && (!e.ye || !e.ye(e.T.P, e.ue)))) probeFound = true;
347
+ // A lane a correction folded under (D-34): `latest()` shows the hold's
348
+ // world whole — the truth beneath each guess, else what it covered.
349
+ if (latestActive && i.folded) return i.folded(null, e);
350
+ return e.T.P;
351
+ }
352
+ // A first load in flight with nothing produced yet: loading, for
353
+ // `latest` (D-22) as for `isPending` (A19 exc. 1) — unless an error
354
+ // answered it first: its re-ask is pending, the error its display
355
+ // (D-41, #4375).
356
+ if (o && u === e.le && r.m & STATUS_UNINITIALIZED && (latestActive || !r.T.Te)) throw r.T.U;
357
+ if (probing && o) probeFound = true;
358
+ return u;
419
359
  }
420
- // A "not pending" verdict that exists only because this reader saw the
421
- // fresh held value is provisional: if the write turns out NOT to commit
422
- // this flush (a downstream async pends and holds it), the suppression was
423
- // wrong and the wrapper must re-ask (#3028). Remember who to wake — the
424
- // async registration (GlobalQueue.notify) triggers wakeSuppressedProbes.
425
- if (!i.found && i.suppressed.length && context && typeof context.Se === "function") {
426
- for (const e of i.suppressed) {
427
- let t = suppressedProbes.get(e);
428
- if (!t) suppressedProbes.set(e, t = new Set);
429
- t.add(context);
360
+ }
361
+ const o = (r.m & STATUS_UNINITIALIZED) !== 0;
362
+ if (e.l & CONFIG_HELD) {
363
+ if (!o) {
364
+ const i = txOf(e);
365
+ // A render effect re-run (or mounted) outside the verdict lane's flush
366
+ // while the lane is blocked — its display held on its own derivation
367
+ // in flight — is a stale reader of the lane (#3460, as `laneRead`): the
368
+ // committed value now, re-derived at the reveal. Not inside a probe:
369
+ // `isPending` is answered below either way.
370
+ if (n && !probing && t.i === EFFECT_RENDER && !passLane && i.Pe?.ln && flushTransaction !== i) {
371
+ staleReader(t, i.Pe);
372
+ return e.le;
430
373
  }
374
+ if (n) verdictRead(t, i);
375
+ if (probing && heldNotFinal(r)) probeFound = true;
376
+ if (latestActive && e.ue !== NOT_PENDING) {
377
+ // An action body reading another transaction's proposal derives
378
+ // from it: the two settle as one (A15; posture C, 2026-09-15). An
379
+ // untracked read elsewhere has no transaction to entangle.
380
+ if (!n && flushTransaction && !globalQueue.Y) joinFuture(txOf(e));
381
+ return heldLatest(e, n ? t : null);
382
+ }
383
+ return e.le;
431
384
  }
432
- };
433
- try {
434
- e();
435
- collectPending();
436
- return i.found;
437
- } catch (e) {
438
- collectPending();
439
- if (e instanceof NotReadyError) {
440
- const t = !!(e.source?.S & STATUS_UNINITIALIZED);
441
- if (i.found && !t) return true;
442
- if (context && t) throw e;
385
+ // Born into the future: its staging is its only value. It has no
386
+ // committed value, so no verdict: `isPending` reads it as its read does —
387
+ // the owner joins the hold (held with it, or caught by a boundary that
388
+ // has not shown content) and answers for the commit, where it is final.
389
+ if (e.ue !== NOT_PENDING && !(r.m & STATUS_PENDING)) {
390
+ if (probing && i) joinPass(t, e);
391
+ return staged(e);
443
392
  }
444
- return i.found;
445
- } finally {
446
- setPendingCheckActive(t);
447
- pendingProbe = n;
393
+ } else if (e.ue !== NOT_PENDING) {
394
+ if (r.m & STATUS_PENDING && !o) pendingVerdict(n ? t : null, r); else if (n && globalQueue.Y) provisionalVerdict(t, !latestActive && !o);
395
+ // Outside a flush, the staging is an unflushed write (A28): the probe
396
+ // answers for the flushed world, and watches the seam of the flush that
397
+ // carries the write — a memo of `isPending(x)` created after the write
398
+ // flips if that flush parks (#3078). (The write's own fan-out ran before
399
+ // the reader existed; nothing else would re-derive it.)
400
+ else if (n && probing) watchVerdict(t);
401
+ if (latestActive) {
402
+ // A28: an unflushed write is not what `latest` serves; a tracked
403
+ // reader derived from the flushed world runs again in the carrying
404
+ // flush.
405
+ if (globalQueue.Y) return staged(e);
406
+ if (t) markLateLinker(t);
407
+ }
408
+ return e.le;
409
+ }
410
+ if (r.m & STATUS_PENDING) {
411
+ if (!o) {
412
+ pendingVerdict(n ? t : null, r);
413
+ return e.le;
414
+ }
415
+ // As `read()`: an untracked read of a pending node still re-runs its
416
+ // reader when the node settles (a projection's pull reads its firewall
417
+ // untracked, store/store.ts).
418
+ if (t && !tracking && !spectating && e !== t) link(e, t);
419
+ throw r.T?.U;
448
420
  }
421
+ if (e.X && r.m & STATUS_ERROR) throw r.T.U;
422
+ return e.le;
449
423
  }
450
424
 
451
- // Hook installation (same late-binding pattern as GlobalQueue._update /
452
- // _propagateAffects): core call sites fire these behind the same guards the
453
- // direct calls used, so behavior is identical once this module loads.
454
- GlobalQueue.Oe = syncCompanions;
455
-
456
- GlobalQueue.de = updatePendingSignal;
457
-
458
- GlobalQueue.ye = updateChildCompanions;
459
-
460
- GlobalQueue.un = snapCompanionsToState;
461
-
462
- GlobalQueue.Pt = latestRead;
463
-
464
- GlobalQueue.Dt = pendingCheckRead;
465
-
466
- GlobalQueue.Ht = recordFreshRead;
467
-
468
- GlobalQueue.Je = applyReask;
469
-
470
- GlobalQueue.p = repollDownstreamVerdicts;
425
+ /** D-25: a node whose fetch a hold is waiting on is that hold's work, not a
426
+ * verdict reader — a pass that re-runs it reads plainly — and a memo's
427
+ * posture is what it reads: one that reads a verdict reader's value is a
428
+ * verdict reader itself (#4050). An async memo with no verdict window of its
429
+ * own (REACTIVE_WINDOW: it read a verdict memo, it called neither `latest`
430
+ * nor `isPending`) is a verdict reader between its flights, and the hold's
431
+ * work while one is up or its hold re-runs it.
432
+ *
433
+ * At a pass's head (core.ts `recompute`): a pending node's pass — its
434
+ * flight is the transition's own work (#3884) — and a held one's re-run of
435
+ * such a memo read plainly: the write that re-ran it is its hold's, and
436
+ * served the screen it re-asked a question it had answered (A10, #4397).
437
+ * Only a memo that never asked: D-25's "a verdict posture outlives the pass
438
+ * that asked" is a pass that called `latest`/`isPending`, which clears
439
+ * CONFIG_ASYNC and stays a verdict reader whoever dirties it.
440
+ *
441
+ * On `source` going pending (`propagateStatus`): such a memo waits on it
442
+ * like any derivation (F-9) instead of re-deriving — what it read changed
443
+ * no value, so its re-run re-asked the question it was answering. Unless
444
+ * its own flight is up outside a lane on a new question of the source's:
445
+ * its re-run supersedes a flight asked of a value the source has moved past
446
+ * (re-asked by a `refresh()`, the source's question is the same one). */ function holdWork(e, t) {
447
+ // CONFIG_ASYNC records an async memo with no window of its own: its last
448
+ // answering pass registered a flight (`handleAsync`) and called neither
449
+ // `latest` nor `isPending`. A window (`markVerdictReader`), a sync answer or
450
+ // an error clears it; a pass left pending keeps the last answer's.
451
+ const r = e.l & CONFIG_ASYNC;
452
+ return r && (!(e.m & STATUS_PENDING) || e.T.Re?.P || t.R & REACTIVE_REASK);
453
+ }
471
454
 
472
- GlobalQueue.Bt = witnessAffects;
455
+ // Installed at module evaluation — present exactly when something imports
456
+ // `latest` or `isPending`. (`read` dispatches to `verdictValue` through
457
+ // `verdict`, set while a window is open.)
458
+ installVerdictHooks(observeFlight, holdWork);
473
459
 
474
- GlobalQueue.wt = wakeSuppressedProbes;
460
+ /** A store read inside an untracked `isPending` probe found a live mark on
461
+ * a record with no node to carry it (store/affects.ts): not final. */ GlobalQueue.rt = () => {
462
+ if (probing) probeFound = true;
463
+ };
475
464
 
476
465
  export { isPending, latest };