diff --git a/ref/SOLID_NEXT b/ref/SOLID_NEXT index 2edb970..d6eb76f 100644 --- a/ref/SOLID_NEXT +++ b/ref/SOLID_NEXT @@ -1 +1 @@ -0847122e9fc9681fda7feef37d5427db7316e56d +d2323b077c7dda32ebced7843c2ce2b788a91e8d diff --git a/ref/compile/rt-server.ts b/ref/compile/rt-server.ts index 98af70f..609443c 100644 --- a/ref/compile/rt-server.ts +++ b/ref/compile/rt-server.ts @@ -11,6 +11,9 @@ import { provideRequestEvent } from "@solidjs/web/storage"; import { Show, For, Loading, Errored, createComponent, createSignal, createMemo, createEffect, createOptimistic, createOptimisticStore, createStore, createProjection, createDeferred, untrack, latest, isPending, NotReadyError, getOwner, runWithOwner, getNextChildId, runInServerComponentScope } from "solid-js"; import { escape, renderToStream, markSafeError, generateHydrationScript } from "@solidjs/web"; +// 05 H-9 / Solid D-24: `isPending(fn)` judges the reads fn makes, never its return value, so `pending? x` over a structured +// value (a store, a keyed projection) reads its keys — Solid's "whole-store questions are asked through reads" +const readWhole = (v: unknown) => { if (v !== null && typeof v === "object") Reflect.ownKeys(v); return v; }; export type Acc = () => unknown; export type Writer = { set: (v: unknown) => void; path: (keys: (string | number)[], v: unknown) => void; guess: (v: unknown, keys?: (string | number)[]) => void }; export type Entry = { get?: Acc; w?: Writer; value?: unknown }; @@ -207,7 +210,7 @@ export function createApp(opts: { api?: Record unknown> isA, PANIC, catchMemo: catchAcc, tryCatch, catchRead: (f: () => unknown, match: (err: unknown) => boolean, alt: (err: unknown) => unknown) => catchAcc(f, match, alt)(), throw: (e: unknown): never => { throw e; }, eq: (a: unknown, b: unknown) => a === b, - untrack, latest: (f: () => unknown) => latest(f as any), isPending: (f: () => unknown) => isPending(f as any), + untrack, latest: (f: () => unknown) => latest(f as any), isPending: (f: () => unknown) => isPending(() => readWhole(f())), fmt, // ---- views: compiled JSX (compile/jsx.ts, generate "ssr", hydratable) does the rest ---- diff --git a/ref/compile/rt-web.ts b/ref/compile/rt-web.ts index 872d9db..3c3c0e5 100644 --- a/ref/compile/rt-web.ts +++ b/ref/compile/rt-web.ts @@ -12,6 +12,9 @@ import { import { getContext, setContext, $TARGET } from "@solidjs/signals"; import { hydrate, render } from "@solidjs/web"; +// 05 H-9 / Solid D-24: `isPending(fn)` judges the reads fn makes, never its return value, so `pending? x` over a structured +// value (a store, a keyed projection) reads its keys — Solid's "whole-store questions are asked through reads" +const readWhole = (v: unknown) => { if (v !== null && typeof v === "object") Reflect.ownKeys(v); return v; }; export { flush }; export type Acc = () => unknown; export type Writer = { set: (v: unknown) => void; path: (keys: (string | number)[], v: unknown) => void; guess: (v: unknown, keys?: (string | number)[]) => void }; @@ -259,7 +262,7 @@ export function createApp(opts: { root: any; api?: Record unknown, match: (err: unknown) => boolean, alt: (err: unknown) => unknown) => catchAcc(f, match, alt)(), throw: (e: unknown): never => { throw e; }, eq: (a: unknown, b: unknown) => raw(a) === raw(b), - untrack, latest: (f: () => unknown) => latest(f as any), isPending: (f: () => unknown) => isPending(f as any), + untrack, latest: (f: () => unknown) => latest(f as any), isPending: (f: () => unknown) => isPending(() => readWhole(f())), fmt, // ---- views: compiled JSX (compile/jsx.ts, generate "dom") does the rest ---- diff --git a/ref/compile/rt.ts b/ref/compile/rt.ts index 8d5963e..e11d802 100644 --- a/ref/compile/rt.ts +++ b/ref/compile/rt.ts @@ -11,6 +11,9 @@ import { flush, untrack, latest, isPending, action, refresh, affects, until, NotReadyError, createContext, getContext, setContext, getOwner, runWithOwner, $TARGET, createErrorBoundary, } from "@solidjs/signals"; +// 05 H-9 / Solid D-24: `isPending(fn)` judges the reads fn makes, never its return value, so `pending? x` over a structured +// value (a store, a keyed projection) reads its keys — Solid's "whole-store questions are asked through reads" +const readWhole = (v: unknown) => { if (v !== null && typeof v === "object") Reflect.ownKeys(v); return v; }; export { flush }; export type Acc = () => unknown; export type Writer = { set: (v: unknown) => void; path: (keys: (string | number)[], v: unknown) => void; guess: (v: unknown, keys?: (string | number)[]) => void }; @@ -266,7 +269,7 @@ export function createApp(opts: { api?: Record unknown> errorClass, isA, PANIC, catchMemo: catchAcc, tryCatch, catchRead: (f: () => unknown, match: (err: unknown) => boolean, alt: (err: unknown) => unknown) => catchAcc(f, match, alt)(), throw: (e: unknown): never => { throw e; }, eq: (a: unknown, b: unknown) => raw(a) === raw(b), - untrack, latest: (f: () => unknown) => latest(f as any), isPending: (f: () => unknown) => isPending(f as any), + untrack, latest: (f: () => unknown) => latest(f as any), isPending: (f: () => unknown) => isPending(() => readWhole(f())), fmt, // ---- views: compiled JSX (compile/jsx.ts) calls the renderer; these are the two helpers it calls on __rt ---- diff --git a/ref/oracle/lower.ts b/ref/oracle/lower.ts index c3e3a09..674337b 100644 --- a/ref/oracle/lower.ts +++ b/ref/oracle/lower.ts @@ -389,7 +389,7 @@ export class Oracle { const v = this.ev(e.e, scope, env); return !e.stream && v instanceof Flight ? v.first : v; // in tail position the memo returns the flight (R-1): a stream for `await*`, else its one answer } // in tail position the memo returns the flight (R-1); elsewhere the operand checks below refuse it case "latest": { const was = this.imperative; this.imperative = 0; try { return latest(() => this.ev(e.e, scope, env)); } finally { this.imperative = was; } } // R-12: `latest` of a value that never had one throws, in a handler as anywhere (Solid S-28) - case "pending": return isPending(() => this.ev(e.e, scope, env)); + case "pending": return isPending(() => { const v = this.ev(e.e, scope, env); if (v !== null && typeof v === "object") Reflect.ownKeys(v); return v; }); // Solid D-24: the probe judges reads, so a structured value is read whole case "if": return this.ev(e.c, scope, env) ? this.ev(e.t, scope, env) : this.ev(e.f, scope, env); case "throw": throw this.ev(e.e, scope, env); // R-f-3 case "serverfn": { let f = this.serverFns.get(e); if (!f) this.serverFns.set(e, (f = this.serverFn({ name: this.serverNames.get(e) ?? "server fn", params: e.params, body: e.body }, scope))); return f; } // SF-1d diff --git a/ref/oracle/repros/README.md b/ref/oracle/repros/README.md index eb0fbe2..0be56b9 100644 --- a/ref/oracle/repros/README.md +++ b/ref/oracle/repros/README.md @@ -120,7 +120,7 @@ re-runs and recovers when the content does (S-fail-1 instants 6–8). --- -## DV-8 — a keyed projection over a memo stops following it after its first value (mostly fixed on `next` by solidjs/solid#4280; filed as #4271 — what remains is in 05's DV-8 record) +## DV-8 — a keyed projection over a memo stops following it after its first value (fixed on `next` by solidjs/solid#4280; filed as #4271 — the two cases left in S-ai2-search were our lowering, `dv8b-projection-pending.mjs`) Found by an agent-written app (S-ai2-search): `results key id = q.length < 2 ? throw TooShort(q) : await search(q)`, lowered as @@ -145,7 +145,7 @@ in Solid; with `key`, the interpreter does too. --- -## DV-9 — a failed boundary renders no fallback on the server (a Solid bug, ruled 2026-10-26; solidjs/solid#4287) +## DV-9 — a failed boundary renders no fallback on the server (a Solid bug, ruled 2026-10-26; solidjs/solid#4287, fixed on `next`) **Rule.** 10-server SV-6: a boundary that takes a failure during the server render is streamed with its fallback drawn, so the browser shows the @@ -183,7 +183,7 @@ hydrated "loading" --- -## DV-10 — a held lane holds per render effect (reversed 2026-10-26: Solid's position adopted; solidjs/solid#4284, open) +## DV-10 — a held lane holds per render effect (reversed 2026-10-26: Solid's position adopted; solidjs/solid#4284, closed; the `one` mode is fixed on `next` at `2c9fdd9`) **Rule.** 05 H-7 (since 2026-10-26, Solid's position): a tracked `latest x` of an optimistic guess reads ahead of the guess's held lane; the lane's @@ -281,7 +281,7 @@ between two replayed handlers, so both actions read the committed `n`. --- -## DV-15 — a client question's served fallback is rebuilt on hydration (a Solid bug, ruled 2026-10-26; solidjs/solid#4286) +## DV-15 — a client question's served fallback is rebuilt on hydration (a Solid bug, ruled 2026-10-26; solidjs/solid#4286, fixed on `next`) **Rule.** 10 SV-3a: hydration adopts what the server sent; SV-7b: a client question's reader shows its hold's fallback on the server, and the browser @@ -355,7 +355,7 @@ S-solid-5. --- -## DV-18 — a keyed projection's row readers miss every landing after the first (a regression from solidjs/solid#4280) — filed as solidjs/solid#4305 +## DV-18 — a keyed projection's row readers miss every landing after the first (a regression from solidjs/solid#4280) — filed as solidjs/solid#4305, fixed on `next` **Rule.** RULES-DERIVED D-30, spec ST-4 and H-2: each landing reconciles a keyed projection's rows, and its readers show it. @@ -369,3 +369,13 @@ rows still read `"apple,apricot"` (expected `"apple,applesauce,apple pie"`). `direct` (one render effect reading `row.name`) follows both. Bisected: fine at `f811097`, broken at `1212b77` (#4280) and on `2f7dc84`. +--- + +## DV-8b — `pending?` over a keyed projection (ours, not Solid's) + +`dv8b-projection-pending.mjs [proxy|keys|length] [boundary|none]`. The probe +`isPending(() => rows)` reads nothing — Solid judges the reads a probe's +thunk makes, never its return value (RULES-DERIVED D-24, A23) — so it is +`false` while the memo under the projection re-asks. `keys` (what +`__rt.isPending` now does for a structured value) and `length` report +`searching` as S-ai2-search's instants 2 and 4 expect. diff --git a/ref/oracle/repros/dv8b-projection-pending.mjs b/ref/oracle/repros/dv8b-projection-pending.mjs new file mode 100644 index 0000000..84a015c --- /dev/null +++ b/ref/oracle/repros/dv8b-projection-pending.mjs @@ -0,0 +1,23 @@ +// DV-8 (what #4280 left): `isPending` over a keyed projection, read in its own render effect beside a boundary that shows +// the projection, misses its memo's re-ask. Expected (RULES-DERIVED D-30): true while the memo re-asks. +// node --conditions=browser --conditions=development repros/dv8b-projection-pending.mjs [proxy|length] [boundary|none] +import { createRoot, createSignal, createMemo, createProjection, createRenderEffect, createErrorBoundary, createLoadingBoundary, isPending, flush, untrack } from "@solidjs/signals"; +const [probe, where] = [process.argv[2] ?? "proxy", process.argv[3] ?? "boundary"]; +const [q, setQ] = createSignal(""); +const res = {}; let probeShown = "", content = ""; +createRoot(() => { + const src = createMemo(() => { const v = q(); if (v === "") return []; if (v.length < 2) throw new Error("too short"); return new Promise(r => (res[v] = r)); }); + const rows = createProjection(() => src(), [], { key: "id" }); + createRenderEffect(() => isPending(() => probe === "proxy" ? rows : probe === "keys" ? Reflect.ownKeys(rows) : rows.length) ? "searching" : "-", v => { probeShown = v; }); + const view = () => `${rows.length} ${rows.map(r => r.name).join(",")}`; + const out = where === "boundary" ? untrack(() => createErrorBoundary(() => createLoadingBoundary(view, () => "loading")(), e => "error:" + e().message)) : () => { try { return view(); } catch (e) { return e?.message ?? "pending"; } }; + createRenderEffect(() => out(), v => { content = v; }); +}); +const tick = async () => { for (let i = 0; i < 3; i++) { await new Promise(r => setTimeout(r, 0)); flush(); } }; +const show = (l, want) => console.log(l.padEnd(14), probeShown.padEnd(10), JSON.stringify(content), want ? `(probe expected ${want})` : ""); +flush(); await tick(); show("mount"); +setQ("a"); flush(); await tick(); show("q = a (throws)", "-"); +setQ("ap"); flush(); await tick(); show("q = ap", "searching"); +res.ap([{ id: 1, name: "apple" }, { id: 2, name: "apricot" }]); await tick(); show("ap lands", "-"); +setQ("app"); flush(); await tick(); show("q = app", "searching"); +res.app([{ id: 1, name: "apple" }, { id: 3, name: "applesauce" }]); await tick(); show("app lands", "-"); diff --git a/ref/package-lock.json b/ref/package-lock.json index 717e5b5..2d4ea9e 100644 --- a/ref/package-lock.json +++ b/ref/package-lock.json @@ -19,7 +19,7 @@ }, "../../solid-next/packages/compiler": { "name": "@solidjs/compiler", - "version": "2.0.0-rc.18", + "version": "2.0.0-rc.19", "license": "MIT", "devDependencies": { "@emnapi/core": "^1.11.3", @@ -29,17 +29,17 @@ "emnapi": "^1.11.3" }, "optionalDependencies": { - "@solidjs/compiler-darwin-arm64": "2.0.0-rc.18", - "@solidjs/compiler-darwin-x64": "2.0.0-rc.18", - "@solidjs/compiler-linux-arm64-gnu": "2.0.0-rc.18", - "@solidjs/compiler-linux-x64-gnu": "2.0.0-rc.18", - "@solidjs/compiler-wasm32-wasi": "2.0.0-rc.18", - "@solidjs/compiler-win32-x64-msvc": "2.0.0-rc.18" + "@solidjs/compiler-darwin-arm64": "2.0.0-rc.19", + "@solidjs/compiler-darwin-x64": "2.0.0-rc.19", + "@solidjs/compiler-linux-arm64-gnu": "2.0.0-rc.19", + "@solidjs/compiler-linux-x64-gnu": "2.0.0-rc.19", + "@solidjs/compiler-wasm32-wasi": "2.0.0-rc.19", + "@solidjs/compiler-win32-x64-msvc": "2.0.0-rc.19" } }, "../../solid-next/packages/signals": { "name": "@solidjs/signals", - "version": "2.0.0-rc.18", + "version": "2.0.0-rc.19", "license": "MIT", "devDependencies": { "@codspeed/vitest-plugin": "^5.4.0", @@ -62,10 +62,10 @@ }, "../../solid-next/packages/solid": { "name": "solid-js", - "version": "2.0.0-rc.18", + "version": "2.0.0-rc.19", "license": "MIT", "dependencies": { - "@solidjs/signals": "^2.0.0-rc.18", + "@solidjs/signals": "^2.0.0-rc.19", "csstype": "^3.1.0", "seroval": "1.7.0", "seroval-plugins": "1.7.0" @@ -76,7 +76,7 @@ }, "../../solid-next/packages/universal": { "name": "@solidjs/universal", - "version": "2.0.0-rc.18", + "version": "2.0.0-rc.19", "license": "MIT", "devDependencies": { "solid-js": "workspace:*" @@ -85,12 +85,12 @@ "node": ">=22.12.0" }, "peerDependencies": { - "solid-js": "^2.0.0-rc.18" + "solid-js": "^2.0.0-rc.19" } }, "../../solid-next/packages/web": { "name": "@solidjs/web", - "version": "2.0.0-rc.18", + "version": "2.0.0-rc.19", "license": "MIT", "dependencies": { "seroval": "1.7.0", @@ -106,7 +106,7 @@ "node": ">=22.12.0" }, "peerDependencies": { - "solid-js": "^2.0.0-rc.18" + "solid-js": "^2.0.0-rc.19" } }, "node_modules/@esbuild/aix-ppc64": { diff --git a/ref/package.json b/ref/package.json index 55d3708..115629c 100644 --- a/ref/package.json +++ b/ref/package.json @@ -7,7 +7,7 @@ "test": "node --experimental-strip-types run.ts", "oracle": "node --experimental-strip-types --conditions=browser --conditions=development oracle/run.ts", "compiled": "node --experimental-strip-types --conditions=browser --conditions=development compile/run.ts", - "ssr": "node --experimental-strip-types --conditions=development ssr/run.ts", + "ssr": "node --experimental-strip-types ssr/run.ts", "types": "node --experimental-strip-types types/run.ts", "crate": "node --experimental-strip-types crate.ts", "model": "node --experimental-strip-types model.ts" diff --git a/scenarios/S-ai2-search.md b/scenarios/S-ai2-search.md index e931818..cd5cd1c 100644 --- a/scenarios/S-ai2-search.md +++ b/scenarios/S-ai2-search.md @@ -37,15 +37,3 @@ file: ../examples/ai2/search/main.rx - 9: The rejection is an `Error`. The armed `loading` shows content, the inner `NotFound` boundary passes `Error` up (R-f-4), and the outer `fallback(e)` shows the message and the retry button. The list is untouched. - 10: `retry()` is `refresh item`: a quiet re-ask under the same key (AC-4). The error stays the display through the retry (H-12), and readers behind the fallback hold nothing, so the frame is unchanged. - 11: A value replaces the error, so the outer boundary leaves its fallback and the item shows (H-12). Whether the retry action's change was still open (AC-4: staged into it) or not, it commits with this landing. - -## Divergences - -- oracle "Frames" instant 2: `too short: a pick a result` — DV-8, not fully fixed by solidjs/solid#4280: after the projection's memo has failed, its re-ask reports no `pending?` through the projection (a plain memo does: no `key` shows `searching…`). -- oracle "Frames" instant 4: `2 results for app apple apricot pick a result` — DV-8: no `searching…`, the projection reports no pending verdict for its memo's re-ask. -- oracle "Frames" instant 5: `2 results for app apple apricot pick a result` — DV-18: since solidjs/solid#4280, a keyed projection's row readers miss every landing after the first, so the list and the count keep the `ap` rows (`ref/oracle/repros/dv18-projection-relanding.mjs`) -- oracle "Frames" instant 6: `2 results for app apple apricot loading…` — DV-18 -- oracle "Frames" instant 7: `2 results for app apple apricot item 4 not found` — DV-18 -- oracle "Frames" instant 8: `2 results for app apple apricot loading…` — DV-18 -- oracle "Frames" instant 9: `2 results for app apple apricot failed: timeout retry` — DV-18 -- oracle "Frames" instant 10: `2 results for app apple apricot failed: timeout retry` — DV-18 -- oracle "Frames" instant 11: `2 results for app apple apricot applesauce stewed apples` — DV-18 diff --git a/scenarios/S-ai2-settings.md b/scenarios/S-ai2-settings.md index 7ab19fd..3cf087e 100644 --- a/scenarios/S-ai2-settings.md +++ b/scenarios/S-ai2-settings.md @@ -39,8 +39,3 @@ Why, one line per instant: - **9.** A plain local write changes the `density` provision, so both Panels and the Preview update. Nothing else moves. - **10.** A new key (the density differs) means a new flight. The `settings` guess shows now. The `saveError` guess `""` closes the "Save failed" branch now (H-11; a lane condition's branch change attaches immediately, H-7). `pending? settings` is true. - **11.** The answer is written as the truth (`settings := saved`), equal to the guess, so it confirms silently. The plain `saveError := ""` is the truth for that source too, so it does not revert to "quota exceeded". The change commits, and `pending? settings` is false. - -## Divergences - -- server "Frames" instant 1: `` — DV-9: Solid streams an empty fragment for a boundary that fails after the shell; the fallback appears at hydration - diff --git a/scenarios/S-hold-5.md b/scenarios/S-hold-5.md index 88d482d..221262d 100644 --- a/scenarios/S-hold-5.md +++ b/scenarios/S-hold-5.md @@ -29,8 +29,3 @@ Instant 1: the rejection ends the wait like a value (R-6) and reaches the question; the reader behind the error fallback holds nothing (H-4), so the instant commits and the fallback stays (Solid D-39). Instant 3: the answer replaces the error; content returns. - -## Divergences - -- server "Frames" instant 1: `` — DV-9: Solid streams an empty fragment for a boundary that fails after the shell; the fallback appears at hydration - diff --git a/scenarios/S-server-1.md b/scenarios/S-server-1.md index a8a1c0e..6e4fcb9 100644 --- a/scenarios/S-server-1.md +++ b/scenarios/S-server-1.md @@ -69,10 +69,3 @@ generic error in its place, and the boundary shows that — the message never leaves the server. A second request for the page, rendered at the same time, has its own `profile.rx`: these landings are not its landings, and its stream stays at the shell. - -## Divergences - -- server "Server frames" instant 1: `Profile loading posts` — DV-9: Solid streams an empty fragment for a boundary that fails after the shell; the client draws the fallback at hydration -- server "Server frames" instant 2: `Profile` — DV-9 -- server "Frames" instant 1: `Profile loading posts` — DV-9 -- server "Frames" instant 2: `Profile` — DV-9 diff --git a/scenarios/S-server-3.md b/scenarios/S-server-3.md index 2b16432..71e2dfd 100644 --- a/scenarios/S-server-3.md +++ b/scenarios/S-server-3.md @@ -44,12 +44,6 @@ and the hydrated page: the browser draws its answers as ordinary landings. Served, the stream is the mount alone (`zone UTC measuring`): the landings are the browser's, after it has taken the page over. -## Divergences - -- hydrated "Frames" instant 0: `zone UTC measuring` — DV-15: hydrating, - Solid drops the served `

measuring

` and builds its own (the same - text); the rule adopts it (SV-3a). The frames after are the rule's. - ## Rejected variants ``` diff --git a/scenarios/S-solid-38.md b/scenarios/S-solid-38.md index f72cbea..8f6d31b 100644 --- a/scenarios/S-solid-38.md +++ b/scenarios/S-solid-38.md @@ -23,8 +23,3 @@ export default component App() { Instant 2: the write publishes beside the old error; the inner `loading` is hidden behind it — a pending is not a value, and the error fallback never clears to show broken state underneath. - -## Divergences - -- server "Frames" instant 1: `0` — DV-9: Solid streams an empty fragment for a boundary that fails after the shell; the fallback appears at hydration - diff --git a/spec/05-holds.md b/spec/05-holds.md index d5893ca..f29be60 100644 --- a/spec/05-holds.md +++ b/spec/05-holds.md @@ -284,7 +284,8 @@ repro in `ref/oracle/repros/` still reproduces on that commit. and the probe freezes the probe. Solid is the source for holds, so H-7 and H-11 now say so and the frame is `1 x1 / 5`. The open part of #4284 (the one-effect grouping, the repro's `one` mode) shows in no scenario: - every `{…}` is its own effect. + every `{…}` is its own effect. Fixed on `next` too: at `2c9fdd9` the + repro's `one` mode shows `1 x1 / 5`. - **DV-13 (an empty fragment in the universal renderer) — a Solid bug, reproduced 2026-10-24; fixed on `next` 2026-10-26 (solidjs/solid#4274, fix #4276)** (`ref/oracle/repros/dv13-universal-empty.mjs`, found by S-ai2-search once its views went through Solid's compiler). In @@ -327,10 +328,15 @@ repro in `ref/oracle/repros/` still reproduces on that commit. `pending?`. The spec frames stand (the interpreter matches the agent's predictions, and the same app without `key` matches in Solid). Since the fix the projection reports its memo's errors and its re-ask's - `pending?`; two cases remain, both in S-ai2-search. After the memo has - failed, the re-ask reports no `pending?` through the projection (reduced - to signals alone; a plain memo reports it). At instant 4, a re-ask after - a landing shows no `searching…` either; that one is not reduced yet. + `pending?`. The two cases left in S-ai2-search (instants 2 and 4: no + `searching…`) were ours, not Solid's (2026-10-27, + `ref/oracle/repros/dv8b-projection-pending.mjs`): `pending? results` + lowered to `isPending(() => proj)`, and Solid's probe judges the reads + its thunk makes, never its return value — `isPending(() => store)` reads + nothing and is `false` by design (D-24, A23). `pending?` over a + structured value now reads its keys (`__rt.isPending`, the oracle's + `pending` case), Solid's "whole-store questions are asked through + reads"; S-ai2-search matches on every runner. - **DV-18 (a keyed projection's rows miss later landings) — a Solid bug, reproduced 2026-10-26** (`ref/oracle/repros/dv18-projection-relanding.mjs`, found by S-ai2-search on the repin). A regression from DV-8's fix @@ -339,7 +345,8 @@ repro in `ref/oracle/repros/` still reproduces on that commit. landing, a memo per row — what a `for` row's `{r.name}` is — never sees a later landing, so the list and everything held with it keep the old rows. A render effect reading the rows directly follows. The spec frames - stand. Filed as solidjs/solid#4305. + stand. Filed as solidjs/solid#4305; fixed on `next` 2026-10-27 (shows + in no scenario at `2c9fdd9`). - **DV-4 (S-37 row 2 under a boundary) — a Solid bug, reproduced 2026-10-23; fixed on `next` 2026-10-26 (solidjs/solid#4269, fix #4278)** (`ref/oracle/repros/dv4-signals.mjs`). Under a `` that has shown content, a derived store's per-key verdicts diff --git a/spec/10-server.md b/spec/10-server.md index 37cf0bd..062c044 100644 --- a/spec/10-server.md +++ b/spec/10-server.md @@ -164,7 +164,8 @@ Each has a standalone repro with nothing of ReactiveScript in it — one `ref/oracle/repros/`, re-run on `next` @ `0847122` (2026-10-26). - **DV-9 (a failed boundary on the server) — a Solid bug, ruled - 2026-10-26 (solidjs/solid#4287); reproduced 2026-10-24** + 2026-10-26 (solidjs/solid#4287); reproduced 2026-10-24; fixed on `next` + 2026-10-26 (shows in no scenario at `2c9fdd9`)** (`dv9-ssr-after-shell.mjs`, `dv9-hydrate/`). Outside a server component, Solid renders no error fallback for a failure that reaches a `` through an async read. A boundary that fails before the shell is empty in @@ -176,8 +177,12 @@ Each has a standalone repro with nothing of ReactiveScript in it — one shows the loading fallback, never the failure it was sent. Inside `runInServerComponentScope` the fallback is rendered on the server and every served frame is the client's (`SV_SCOPE=1 npm run -s ssr`). - Recorded as `server` divergences in S-server-1, S-hold-5, S-solid-38 and - S-ai2-settings; their hydrations after the failure are reported `≠ DV-9`. + It was recorded as `server` divergences in S-server-1, S-hold-5, + S-solid-38 and S-ai2-settings; those records were removed 2026-10-27: + at `2c9fdd9` the server draws the fallback. Solid's development server + build draws a panic's own message there (it keeps fidelity by design); + its production build sends the generic error SV-5b asks for, so the + `ssr` runner now runs the production server build. (A failure thrown synchronously in content the server renders is drawn — S-hold-6 serves `item 4 not found`.) - **DV-11 (the hydration window) — reversed 2026-10-26: Solid's position @@ -211,14 +216,16 @@ Each has a standalone repro with nothing of ReactiveScript in it — one divergence. - **DV-15 (a client question's fallback is rebuilt) — a Solid bug, ruled - 2026-10-26 (solidjs/solid#4286, low priority); reproduced 2026-10-24** (`dv15-client-hole/`). A bare `ssrSource: "client"` memo + 2026-10-26 (solidjs/solid#4286, low priority); reproduced 2026-10-24; + fixed on `next` 2026-10-26 (shows in no scenario at `2c9fdd9`)** (`dv15-client-hole/`). A bare `ssrSource: "client"` memo read under ``: the server sends the fallback and marks the boundary `"$$f"`. Hydrating, the client adopts the fallback, resumes the content, which suspends on the question it now asks, and renders the fallback again — the served element is dropped for an identical one. The frame is right and nothing is asked twice; what is lost is the element (focus, selection, an animation). S-server-3 hydrated, recorded - as a `hydrated` divergence at the hydration instant. + as a `hydrated` divergence at the hydration instant until 2026-10-27; at + `2c9fdd9` the served element is adopted. Ruled 2026-10-26 (Ryan): DV-9, DV-14 and DV-15 are Solid bugs, not intended behaviour — the server draws a failed boundary's fallback and the client