Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-4544-own-staging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/signals": patch
---

A memo whose verdict posture is only inherited (it reads a memo that calls `isPending`/`latest`, and probes nothing itself) is the hold's work where it reads the hold's own write: it reads that staging instead of the pre-hold value, so a branch the hold opens no longer reads the pre-hold world and the hold lands, whether the writes go through one `flush()` or several. A memo that probes in its own body still reads the screen, and what a branch born into the hold then asks for no longer keeps the hold from landing (#4544).
2 changes: 1 addition & 1 deletion packages/signals/docs/RULES-DERIVED.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ An armed boundary catches a lane the same way it catches a transition (D-12, D-1

What is rendered from a display-ahead read reveals together with it: an async memo over `latest(x)` holds the `latest` view's reveal until it lands; a combined `isPending(() => [fast(), copy()])` over two async memos, one wrapped in a sync memo, stays `true` until both land, because a memo's value is one shared slot — it computes under its own hold posture, never under the posture of whichever reader pulled it. A node whose fetch a hold is waiting on is that hold's work, not a verdict reader: a pass that re-runs it while its fetch is in flight reads plainly — following the change it is fetching for — even if an earlier pass of it called `isPending` or `latest` (#3884). A memo's posture is what it reads: one that reads a verdict reader's value is a derivation of a display-ahead read, and a verdict reader itself — `createMemo(() => [held(), V()])` with `V = createMemo(() => isPending(x) && latest(e))` shows `V`'s verdict now, beside the screen's `held()`, as the inline probe and a render effect reading both do (#4050: a Provider's children memo over a router shell). **The probe decides the pass's posture in either read order (clarified 2026-10-28):** a pass that probes is a verdict reader whether the probe came before or after a plain read of the same held node — `[x(), isPending(x)]` and `[isPending(x), x()]` both show `[committed, true]` now and re-derive at the landing (A10) — unless the pass is the hold's own work: born in its world (D-27, #4341) or re-run by the write it holds (#3884 above). Those two read plainly; a probe does not rescue them. A pass that read two holds' display-ahead values is one reveal unit of both (A15 lanes corollary, #3335): it waits for either lane's flight, never showing one hold's `latest()` beside a half-computed derivation of the other's.

**A verdict posture outlives the pass that asked (ruled 2026-10-28):** a pass that asked (`isPending`, `latest`) is the hold's verdict reader on every later run of its node until one completes having asked nothing — that run still reads as a verdict reader; the next is an ordinary derivation. On each such run its plain read of a node the hold holds is the screen's (a frame reader, A10; re-derived at the landing as A15's stale reader) and it joins nothing, whoever dirtied it — a sync write, a boundary redraw, a landing. The hold's own work is not in this: a node whose fetch the hold waits on, or a pass born in its world, reads plainly whatever it asked before (the #3884 clause and D-27 #4341 above, unchanged). A boundary's display-ahead swap is this posture handed on: an `on` pass that read `latest`/`isPending` is a verdict reader, and the fallback swap it asks for runs as that reader's work — the output's pass reads nothing of the held frame (a pending tree is linked, not read) and its answer, the fallback, is shown now beside the held frame and stays the screen until the landing, instead of being staged with the write's frame as a plain `on`'s swap is (D-15): `[A] → [A + spinner] → [B + spinner] → [B + comments]`, never `[A] → [B + spinner]`. Only the `on` pass's asking counts: `latest()` inside the fallback is the fallback's own display-ahead, not the swap's — a plain `on` over an action-held input lands the swap with the frame whatever the fallback reads (#4337). Today two memories carry this — `CONFIG_VERDICT` left on the node by the pass that probed, and the node's listing in the hold's verdict lane (`fallback`'s `b._lane`, `recompute`'s head) — and the rule is the posture, not the membership. Pins: `tests/loading-on-rearm-3540.test.ts`, `tests/loading-on-frame-following-3540.test.ts` §4–5, `tests/loading-on-latest-fallback-4337.test.ts`.
**A verdict posture outlives the pass that asked (ruled 2026-10-28):** a pass that asked (`isPending`, `latest`) is the hold's verdict reader on every later run of its node until one completes having asked nothing — that run still reads as a verdict reader; the next is an ordinary derivation. On each such run its plain read of a node the hold holds is the screen's (a frame reader, A10; re-derived at the landing as A15's stale reader) and it joins nothing, whoever dirtied it — a sync write, a boundary redraw, a landing. The hold's own work is not in this: a node whose fetch the hold waits on, or a pass born in its world, reads plainly whatever it asked before (the #3884 clause and D-27 #4341 above, unchanged). A boundary's display-ahead swap is this posture handed on: an `on` pass that read `latest`/`isPending` is a verdict reader, and the fallback swap it asks for runs as that reader's work — the output's pass reads nothing of the held frame (a pending tree is linked, not read) and its answer, the fallback, is shown now beside the held frame and stays the screen until the landing, instead of being staged with the write's frame as a plain `on`'s swap is (D-15): `[A] → [A + spinner] → [B + spinner] → [B + comments]`, never `[A] → [B + spinner]`. Only the `on` pass's asking counts: `latest()` inside the fallback is the fallback's own display-ahead, not the swap's — a plain `on` over an action-held input lands the swap with the frame whatever the fallback reads (#4337). Today two memories carry this — `CONFIG_VERDICT` left on the node by the pass that probed, and the node's listing in the hold's verdict lane (`fallback`'s `b._lane`, `recompute`'s head) — and the rule is the posture, not the membership. Pins: `tests/loading-on-rearm-3540.test.ts`, `tests/loading-on-frame-following-3540.test.ts` §4–5, `tests/loading-on-latest-fallback-4337.test.ts`. **An inherited posture is not a probe (2026-11-02, #4544):** a pass whose verdict posture is only inherited — it read a verdict memo, and neither it nor its node's last pass opened a window of its own — is the hold's work where it reads the hold's own write (a sync write the hold holds, not a flight's answer): it is served that staging and joins the hold, as a plain derivation does. One flush or several make no difference (F-10): before the flush parks, the pass reads the flush's own write as that staging, leaving the verdict lane an earlier read of the verdict memo seated it in, as the other read order keeps it out. Not a staging a window asked of under the hold that holds it (or in this flush, before one): `[latest(x), x()]` through a memo stays atomic (A10, D-26: `tests/latest-memo-inline-parity.test.ts`). That mark is the hold's — it expires when the hold lands or dissolves, and a later hold reads the node as never asked. A pass that probes in its own body stays the hold's verdict reader and reads the screen (F-6 over a staging it holds). When a frame born into the hold (a render effect never committed) reads such a reader's settled value and goes pending on what that display-ahead value asks for, the pending does not hold the hold (F-6: display-ahead readers never hold the change they read ahead of): the hold lands, the reader re-derives at the landing (A15) and the frame with it. A lane's reveal still waits on it (S-29). Pins: `tests/verdict-memo-held-branch-4544.test.ts`.

**Collapses:** [A31](./SPEC-ASYNC-SEMANTICS.md#a31-a-memo-computes-under-its-own-lane-posture-never-its-pullers) (#3442), §20.1 question 3 ("Yeah definitely it is held. Consistent with any optimistic update"), OL-R24/R25/R34, recovery 6.2. **Status:** standing. A memo over a verdict memo: `tests/verdict-memo-under-held-memo-4050.test.ts`, `packages/web/test/held-shell-pending-feedback-4050.spec.tsx` (U-10). **Fixed 2026-11-01:** the verdict shortcut in `propagateStatus` (a probed source settling re-derives its verdict reader) now clears the settled source from the reader's pending sources — left in place, an errored probe target stranded a reader re-parked on a second source at that source's landing; `tests/verdict-shortcut-pending-source-liveness.test.ts`.

Expand Down
Loading
Loading