`/`` wrappers no matter how well-formed the markup — so once #3099 made validate failures compile errors, a root component owning the document shell failed to compile in plain client mode, and merely importing it (the jsdom component-test configuration) was fatal. Shell-rooted templates now parse as a document and the shell element is compared back — the analogue of the synthetic `
` wrap for table partials, in both the Babel plugin and the native compiler. Genuine restructuring (an implied ``, flow content in ``, a `
` split in `
`) still errors. Since `` parsing flattens shells, actually client-creating one now throws a descriptive dev-mode error from `template()` pointing at `hydrate()` — the failure moved from every import to the one broken act.
+- d601119: Remove the experimental patch channel and patch-mode list driver (always opt-in, never default). Graph-native regions own value delivery and the unified-For design owns list structure, so the channel's parallel delivery machinery is retired: `patch.ts`/`patch-driver.ts` deleted, the compiler-contract exports (`registerPatch`/`registerRowOps`/`registerSlotPatch`/`patchableRaw`, `patchDriver`/`rowProof`/`driveList`) removed, the `patchDriver` compiler option dropped from both compilers, the insert `$ll` seam stripped, and the write-side channel struct dieted to the single written-keys bound (`t.wk`) the core fold/notify paths actually use. Store-family app bundles reclaim up to ~900 B brotli; every measured tier shrinks.
+
## 2.0.0-rc.6
### Patch Changes
diff --git a/packages/babel-plugin/package.json b/packages/babel-plugin/package.json
index 6239c23ec..fdf651f29 100644
--- a/packages/babel-plugin/package.json
+++ b/packages/babel-plugin/package.json
@@ -1,7 +1,7 @@
{
"name": "@solidjs/babel-plugin",
"description": "Babel compiler plugin for Solid templates",
- "version": "2.0.0-rc.6",
+ "version": "2.0.0-rc.7",
"author": "Ryan Carniato",
"license": "MIT",
"repository": {
diff --git a/packages/compiler/CHANGELOG.md b/packages/compiler/CHANGELOG.md
index a8a7df6e8..16f32d77c 100644
--- a/packages/compiler/CHANGELOG.md
+++ b/packages/compiler/CHANGELOG.md
@@ -1,5 +1,13 @@
# @solidjs/compiler
+## 2.0.0-rc.7
+
+### Patch Changes
+
+- ead7b1a: Keep hydration IDs aligned when an intrinsic element has a ref and one reactive spread.
+- b3586e8: Validate document-shell templates in the document context (#3259). The `validate` pass round-trips templates through a body-context fragment parse, which strips ``/``/`` wrappers no matter how well-formed the markup — so once #3099 made validate failures compile errors, a root component owning the document shell failed to compile in plain client mode, and merely importing it (the jsdom component-test configuration) was fatal. Shell-rooted templates now parse as a document and the shell element is compared back — the analogue of the synthetic `
` wrap for table partials, in both the Babel plugin and the native compiler. Genuine restructuring (an implied ``, flow content in ``, a `
` split in `
`) still errors. Since `` parsing flattens shells, actually client-creating one now throws a descriptive dev-mode error from `template()` pointing at `hydrate()` — the failure moved from every import to the one broken act.
+- d601119: Remove the experimental patch channel and patch-mode list driver (always opt-in, never default). Graph-native regions own value delivery and the unified-For design owns list structure, so the channel's parallel delivery machinery is retired: `patch.ts`/`patch-driver.ts` deleted, the compiler-contract exports (`registerPatch`/`registerRowOps`/`registerSlotPatch`/`patchableRaw`, `patchDriver`/`rowProof`/`driveList`) removed, the `patchDriver` compiler option dropped from both compilers, the insert `$ll` seam stripped, and the write-side channel struct dieted to the single written-keys bound (`t.wk`) the core fold/notify paths actually use. Store-family app bundles reclaim up to ~900 B brotli; every measured tier shrinks.
+
## 2.0.0-rc.6
### Patch Changes
diff --git a/packages/compiler/npm/darwin-arm64/package.json b/packages/compiler/npm/darwin-arm64/package.json
index b1736ac6c..9cb4a2476 100644
--- a/packages/compiler/npm/darwin-arm64/package.json
+++ b/packages/compiler/npm/darwin-arm64/package.json
@@ -1,6 +1,6 @@
{
"name": "@solidjs/compiler-darwin-arm64",
- "version": "2.0.0-rc.6",
+ "version": "2.0.0-rc.7",
"cpu": [
"arm64"
],
diff --git a/packages/compiler/npm/darwin-x64/package.json b/packages/compiler/npm/darwin-x64/package.json
index 25016069f..bb9173c0b 100644
--- a/packages/compiler/npm/darwin-x64/package.json
+++ b/packages/compiler/npm/darwin-x64/package.json
@@ -1,6 +1,6 @@
{
"name": "@solidjs/compiler-darwin-x64",
- "version": "2.0.0-rc.6",
+ "version": "2.0.0-rc.7",
"cpu": [
"x64"
],
diff --git a/packages/compiler/npm/linux-arm64-gnu/package.json b/packages/compiler/npm/linux-arm64-gnu/package.json
index c1f7deb9a..cf6cd0c6c 100644
--- a/packages/compiler/npm/linux-arm64-gnu/package.json
+++ b/packages/compiler/npm/linux-arm64-gnu/package.json
@@ -1,6 +1,6 @@
{
"name": "@solidjs/compiler-linux-arm64-gnu",
- "version": "2.0.0-rc.6",
+ "version": "2.0.0-rc.7",
"cpu": [
"arm64"
],
diff --git a/packages/compiler/npm/linux-x64-gnu/package.json b/packages/compiler/npm/linux-x64-gnu/package.json
index dd84a7dd6..af9123905 100644
--- a/packages/compiler/npm/linux-x64-gnu/package.json
+++ b/packages/compiler/npm/linux-x64-gnu/package.json
@@ -1,6 +1,6 @@
{
"name": "@solidjs/compiler-linux-x64-gnu",
- "version": "2.0.0-rc.6",
+ "version": "2.0.0-rc.7",
"cpu": [
"x64"
],
diff --git a/packages/compiler/npm/wasm32-wasi/package.json b/packages/compiler/npm/wasm32-wasi/package.json
index 9ddef70b5..a83cb3398 100644
--- a/packages/compiler/npm/wasm32-wasi/package.json
+++ b/packages/compiler/npm/wasm32-wasi/package.json
@@ -1,6 +1,6 @@
{
"name": "@solidjs/compiler-wasm32-wasi",
- "version": "2.0.0-rc.6",
+ "version": "2.0.0-rc.7",
"main": "compiler.wasi.cjs",
"files": [
"compiler.wasm32-wasi.wasm",
diff --git a/packages/compiler/npm/win32-x64-msvc/package.json b/packages/compiler/npm/win32-x64-msvc/package.json
index ab6c60dbf..4daaeaa0f 100644
--- a/packages/compiler/npm/win32-x64-msvc/package.json
+++ b/packages/compiler/npm/win32-x64-msvc/package.json
@@ -1,6 +1,6 @@
{
"name": "@solidjs/compiler-win32-x64-msvc",
- "version": "2.0.0-rc.6",
+ "version": "2.0.0-rc.7",
"cpu": [
"x64"
],
diff --git a/packages/compiler/package.json b/packages/compiler/package.json
index e64d301ee..00d83514d 100644
--- a/packages/compiler/package.json
+++ b/packages/compiler/package.json
@@ -1,7 +1,7 @@
{
"name": "@solidjs/compiler",
"description": "Solid's native Oxc JSX compiler",
- "version": "2.0.0-rc.6",
+ "version": "2.0.0-rc.7",
"author": "Ryan Carniato",
"license": "MIT",
"repository": {
@@ -62,11 +62,11 @@
"emnapi": "^1.11.3"
},
"optionalDependencies": {
- "@solidjs/compiler-darwin-x64": "2.0.0-rc.6",
- "@solidjs/compiler-darwin-arm64": "2.0.0-rc.6",
- "@solidjs/compiler-linux-x64-gnu": "2.0.0-rc.6",
- "@solidjs/compiler-linux-arm64-gnu": "2.0.0-rc.6",
- "@solidjs/compiler-win32-x64-msvc": "2.0.0-rc.6",
- "@solidjs/compiler-wasm32-wasi": "2.0.0-rc.6"
+ "@solidjs/compiler-darwin-x64": "2.0.0-rc.7",
+ "@solidjs/compiler-darwin-arm64": "2.0.0-rc.7",
+ "@solidjs/compiler-linux-x64-gnu": "2.0.0-rc.7",
+ "@solidjs/compiler-linux-arm64-gnu": "2.0.0-rc.7",
+ "@solidjs/compiler-win32-x64-msvc": "2.0.0-rc.7",
+ "@solidjs/compiler-wasm32-wasi": "2.0.0-rc.7"
}
}
diff --git a/packages/diagnostics/CHANGELOG.md b/packages/diagnostics/CHANGELOG.md
index effc46bf4..14bde8db8 100644
--- a/packages/diagnostics/CHANGELOG.md
+++ b/packages/diagnostics/CHANGELOG.md
@@ -1,5 +1,64 @@
# @solidjs/diagnostics
+## 2.0.0-rc.7
+
+### Patch Changes
+
+- 6c8c956: Attribution: `feedback()` gains the fact tables that have no verdict of their own. `flights` counts, per async source, flights started, landed, and abandoned (superseded by a newer flight before landing — the re-ask-on-every-keystroke signature) with landed wall time. `fallbacks` measures, per loading boundary (named by owner path), how many times and for how long its fallback was shown and how many shows were sub-150ms flashes — the other end of the SILENT_HOLD spectrum. `sources` rows gain `late`/`lateMs`: acknowledged holds that still ran past `holds.infoMs`, where the affordance is not the whole answer. New `AttributionHooks.boundaryFallback` hook point at the boundary's source-set transitions. `@solidjs/diagnostics` exports the `FlightStats` and `FallbackStats` types; artifacts and the bridge carry the new tables through the existing `feedback` field.
+- d5aba4b: `WIDE_WRITE` and `HOT_SCOPE_FANOUT` diagnostics, and the reactivity-diagnostics and agent-loops skills, now prescribe a projection (`createProjection`, or a `createStore(fn)` keyed by id) as the fan-out repair. They previously named an API that is not part of 2.0 (#3304).
+- 1a1e2f2: Diagnostics: the responsiveness gate — holds and feedback in the artifact, `expectNoSilentHolds`, and Loop 4.
+
+ The artifact (format v2) now carries `attribution.holds` — every transition hold the scenario caused, with the held writes, the blockers, the wait measured from the interaction, and which affordances acknowledged it — and `attribution.feedback`, the ranked `sources`/`interactions` tables folded from them. JSONL egress emits `hold` and `feedback` records; the browser bridge and the `/__solid/diagnostics` protocol gain `holds()` and `feedback()` live queries.
+
+ New gates: `expectNoSilentHolds(artifact, { maxSilentMs })` fails on any hold the screen never acknowledged (no `isPending()`/`latest()` reader, no optimistic value, no `affects()` mark, nothing painted) with the interaction, held write, blocker, and duration as evidence; `expectHoldBudget(artifact, ms, { source })` bounds hold latency regardless of acknowledgment. `ScenarioBudget` gains `maxSilentHoldMs` and `maxHoldMs`; Vitest gains `toHaveNoSilentHolds()` and `toStayWithinHoldBudget(ms)`. The agent-loops skill gains "Loop 4 — Responsiveness": read `feedback.sources` first, repair by shape (`isPending` → `latest` → `createOptimistic`), and the explicit anti-repair — never make the gate pass by moving the write off the async path. Types `HoldEvent`, `ChangeOrigin`, `AttributionFeedback`, `FeedbackSource`, `FeedbackInteraction` are exported.
+
+- f4d3c87: Responsiveness thresholds and the LONG_HOLD diagnostic.
+ - `SILENT_HOLD` defaults tighten to `holds: { infoMs: 100, warnMs: 200 }` (from 300/500): RAIL's "feels instant" ceiling and the INP "good" ceiling. The engine measures to the commit, not the paint, so every number is a floor on what the user saw; the console's thresholds now sit at the strict end of the band.
+ - New `LONG_HOLD` (`responsiveness` kind): an acknowledged hold whose quiescent tail — from the last write to join it to the commit — reached `longHolds.infoMs` (default 500ms), `warn` from `longHolds.warnMs` (1000ms). Measured from the last join so a hold that keeps taking input is judged by each wait, not its lifetime. The repair is a fallback: a `Loading` boundary keyed with `on` (a revealed boundary without `on` keeps the old content — that is the hold), a fresh boundary, or making the data fast. A silent long hold stays one `SILENT_HOLD` with the same repair appended and `data.long: true`.
+ - `HoldEvent.tailMs` added; `holdMs` now runs from the interaction dispatch or the first parked flush, whichever is earlier (a node rewritten mid-hold keeps only its latest record, so the flush clock keeps the first wait from being forgotten). `ChangeRecord.at` stamps root writes.
+ - `feedback().sources[].late/lateMs` replaced by `long/longMs`: holds whose tail reached the long-hold threshold, acknowledged or not.
+ - `@solidjs/diagnostics` artifact format version 3 (`tailMs` on holds, `long`/`longMs` on sources); hold evidence in assertion failures includes `tailMs`.
+ - `RerunEvent.phase` value `"transition"` renamed to `"held"` (`"plain" | "held" | "optimistic"`) — the dev surface uses one word for the state.
+
+- Updated dependencies [215de3b]
+- Updated dependencies [6c8c956]
+- Updated dependencies [1a1e2f2]
+- Updated dependencies [7c14e23]
+- Updated dependencies [ef2b02c]
+- Updated dependencies [c6c415b]
+- Updated dependencies [6c8c956]
+- Updated dependencies [d5aba4b]
+- Updated dependencies [3ae0ca0]
+- Updated dependencies [ae46c92]
+- Updated dependencies [6c8c956]
+- Updated dependencies [1a1e2f2]
+- Updated dependencies [f98bd77]
+- Updated dependencies [fc7e626]
+- Updated dependencies [d50e855]
+- Updated dependencies [8f9f369]
+- Updated dependencies [c531e2a]
+- Updated dependencies [aed21ac]
+- Updated dependencies [b3c94be]
+- Updated dependencies [0653673]
+- Updated dependencies [6c8c956]
+- Updated dependencies [94fe5b4]
+- Updated dependencies [23477ae]
+- Updated dependencies [f4d3c87]
+- Updated dependencies [067e3bc]
+- Updated dependencies [8a65e5e]
+- Updated dependencies [f24e53d]
+- Updated dependencies [fa568d3]
+- Updated dependencies [d601119]
+- Updated dependencies [ac5159a]
+- Updated dependencies [de1c8b5]
+- Updated dependencies [01e3a57]
+- Updated dependencies [e346e61]
+- Updated dependencies [713a910]
+- Updated dependencies [e346e61]
+- Updated dependencies [0255729]
+- Updated dependencies [6c8c956]
+ - @solidjs/signals@2.0.0-rc.7
+
## 2.0.0-rc.6
### Patch Changes
diff --git a/packages/diagnostics/package.json b/packages/diagnostics/package.json
index 98a0a60b9..7998722e8 100644
--- a/packages/diagnostics/package.json
+++ b/packages/diagnostics/package.json
@@ -1,6 +1,6 @@
{
"name": "@solidjs/diagnostics",
- "version": "2.0.0-rc.6",
+ "version": "2.0.0-rc.7",
"description": "Agent-consumable diagnostics harness for Solid: capture the dev-mode diagnostic and attribution channels as structured artifacts, with assertion helpers for tests and agent loops.",
"author": "Ryan Carniato",
"license": "MIT",
@@ -52,7 +52,7 @@
"test:watch": "vitest watch tests"
},
"dependencies": {
- "@solidjs/signals": "^2.0.0-rc.6"
+ "@solidjs/signals": "^2.0.0-rc.7"
},
"peerDependencies": {
"vitest": ">=2.0.0"
diff --git a/packages/element/CHANGELOG.md b/packages/element/CHANGELOG.md
index 68cb1935b..18e8b4bbd 100644
--- a/packages/element/CHANGELOG.md
+++ b/packages/element/CHANGELOG.md
@@ -1,5 +1,59 @@
# solid-element
+## 2.0.0-rc.7
+
+### Patch Changes
+
+- Updated dependencies [215de3b]
+- Updated dependencies [1a1e2f2]
+- Updated dependencies [7c14e23]
+- Updated dependencies [1226381]
+- Updated dependencies [7009adf]
+- Updated dependencies [ef2b02c]
+- Updated dependencies [a14c138]
+- Updated dependencies [c6c415b]
+- Updated dependencies [6c8c956]
+- Updated dependencies [d5aba4b]
+- Updated dependencies [3ae0ca0]
+- Updated dependencies [1a1e2f2]
+- Updated dependencies [c08e974]
+- Updated dependencies [6c8c956]
+- Updated dependencies [1a1e2f2]
+- Updated dependencies [fbe5bef]
+- Updated dependencies [292bdc5]
+- Updated dependencies [285a717]
+- Updated dependencies [8f11ea7]
+- Updated dependencies [d0ca3a4]
+- Updated dependencies [b6a90f9]
+- Updated dependencies [3424f9a]
+- Updated dependencies [6164667]
+- Updated dependencies [b64071c]
+- Updated dependencies [b3586e8]
+- Updated dependencies [f21e060]
+- Updated dependencies [ecdfc3a]
+- Updated dependencies [5cee0f7]
+- Updated dependencies [6c8c956]
+- Updated dependencies [6bb51c9]
+- Updated dependencies [f4d3c87]
+- Updated dependencies [ff2ecf1]
+- Updated dependencies [c0bc9ba]
+- Updated dependencies [ace227e]
+- Updated dependencies [b7b17ab]
+- Updated dependencies [ed6b605]
+- Updated dependencies [6c9f8f4]
+- Updated dependencies [3393fb6]
+- Updated dependencies [d601119]
+- Updated dependencies [ac5159a]
+- Updated dependencies [ace227e]
+- Updated dependencies [de1c8b5]
+- Updated dependencies [d2b50e9]
+- Updated dependencies [84a94bc]
+- Updated dependencies [01e3a57]
+- Updated dependencies [a1ff286]
+- Updated dependencies [6c8c956]
+ - solid-js@2.0.0-rc.7
+ - @solidjs/web@2.0.0-rc.7
+
## 2.0.0-rc.6
### Patch Changes
diff --git a/packages/element/package.json b/packages/element/package.json
index a8a112fc9..db10fb65a 100644
--- a/packages/element/package.json
+++ b/packages/element/package.json
@@ -3,7 +3,7 @@
"description": "Webcomponents wrapper for Solid",
"author": "Ryan Carniato",
"license": "MIT",
- "version": "2.0.0-rc.6",
+ "version": "2.0.0-rc.7",
"homepage": "https://github.com/solidjs/solid/blob/main/packages/element#readme",
"repository": {
"type": "git",
@@ -30,8 +30,8 @@
"component-register": "^0.8.7"
},
"peerDependencies": {
- "solid-js": "^2.0.0-rc.6",
- "@solidjs/web": "^2.0.0-rc.6"
+ "solid-js": "^2.0.0-rc.7",
+ "@solidjs/web": "^2.0.0-rc.7"
},
"devDependencies": {
"solid-js": "workspace:*",
diff --git a/packages/h/CHANGELOG.md b/packages/h/CHANGELOG.md
index 1a30372a1..b6c4703c3 100644
--- a/packages/h/CHANGELOG.md
+++ b/packages/h/CHANGELOG.md
@@ -1,5 +1,43 @@
# @solidjs/h
+## 2.0.0-rc.7
+
+### Patch Changes
+
+- Updated dependencies [7c14e23]
+- Updated dependencies [1226381]
+- Updated dependencies [7009adf]
+- Updated dependencies [ef2b02c]
+- Updated dependencies [a14c138]
+- Updated dependencies [6c8c956]
+- Updated dependencies [c08e974]
+- Updated dependencies [fbe5bef]
+- Updated dependencies [292bdc5]
+- Updated dependencies [285a717]
+- Updated dependencies [8f11ea7]
+- Updated dependencies [d0ca3a4]
+- Updated dependencies [b6a90f9]
+- Updated dependencies [6164667]
+- Updated dependencies [b64071c]
+- Updated dependencies [b3586e8]
+- Updated dependencies [f21e060]
+- Updated dependencies [ecdfc3a]
+- Updated dependencies [5cee0f7]
+- Updated dependencies [6bb51c9]
+- Updated dependencies [ff2ecf1]
+- Updated dependencies [c0bc9ba]
+- Updated dependencies [ace227e]
+- Updated dependencies [b7b17ab]
+- Updated dependencies [ed6b605]
+- Updated dependencies [6c9f8f4]
+- Updated dependencies [3393fb6]
+- Updated dependencies [d601119]
+- Updated dependencies [ace227e]
+- Updated dependencies [d2b50e9]
+- Updated dependencies [84a94bc]
+- Updated dependencies [a1ff286]
+ - @solidjs/web@2.0.0-rc.7
+
## 2.0.0-rc.6
### Patch Changes
diff --git a/packages/h/package.json b/packages/h/package.json
index cc7e11913..fefb5f2ff 100644
--- a/packages/h/package.json
+++ b/packages/h/package.json
@@ -1,7 +1,7 @@
{
"name": "@solidjs/h",
"description": "Hyperscript / h() factory for Solid — write components without compiled JSX.",
- "version": "2.0.0-rc.6",
+ "version": "2.0.0-rc.7",
"author": "Ryan Carniato",
"license": "MIT",
"homepage": "https://solidjs.com",
@@ -76,7 +76,7 @@
"test": "vitest run"
},
"peerDependencies": {
- "@solidjs/web": "^2.0.0-rc.6"
+ "@solidjs/web": "^2.0.0-rc.7"
},
"devDependencies": {
"@solidjs/web": "workspace:*",
diff --git a/packages/html/CHANGELOG.md b/packages/html/CHANGELOG.md
index 1bc0a4a3f..a0bf3477c 100644
--- a/packages/html/CHANGELOG.md
+++ b/packages/html/CHANGELOG.md
@@ -1,5 +1,43 @@
# @solidjs/html
+## 2.0.0-rc.7
+
+### Patch Changes
+
+- Updated dependencies [7c14e23]
+- Updated dependencies [1226381]
+- Updated dependencies [7009adf]
+- Updated dependencies [ef2b02c]
+- Updated dependencies [a14c138]
+- Updated dependencies [6c8c956]
+- Updated dependencies [c08e974]
+- Updated dependencies [fbe5bef]
+- Updated dependencies [292bdc5]
+- Updated dependencies [285a717]
+- Updated dependencies [8f11ea7]
+- Updated dependencies [d0ca3a4]
+- Updated dependencies [b6a90f9]
+- Updated dependencies [6164667]
+- Updated dependencies [b64071c]
+- Updated dependencies [b3586e8]
+- Updated dependencies [f21e060]
+- Updated dependencies [ecdfc3a]
+- Updated dependencies [5cee0f7]
+- Updated dependencies [6bb51c9]
+- Updated dependencies [ff2ecf1]
+- Updated dependencies [c0bc9ba]
+- Updated dependencies [ace227e]
+- Updated dependencies [b7b17ab]
+- Updated dependencies [ed6b605]
+- Updated dependencies [6c9f8f4]
+- Updated dependencies [3393fb6]
+- Updated dependencies [d601119]
+- Updated dependencies [ace227e]
+- Updated dependencies [d2b50e9]
+- Updated dependencies [84a94bc]
+- Updated dependencies [a1ff286]
+ - @solidjs/web@2.0.0-rc.7
+
## 2.0.0-rc.6
### Patch Changes
diff --git a/packages/html/package.json b/packages/html/package.json
index 59fcd5d59..9f8bfb6f6 100644
--- a/packages/html/package.json
+++ b/packages/html/package.json
@@ -1,7 +1,7 @@
{
"name": "@solidjs/html",
"description": "Tagged-template-literal templating for Solid — write components with no build step.",
- "version": "2.0.0-rc.6",
+ "version": "2.0.0-rc.7",
"author": "Ryan Carniato",
"license": "MIT",
"homepage": "https://solidjs.com",
@@ -47,7 +47,7 @@
"test": "vitest run"
},
"peerDependencies": {
- "@solidjs/web": "^2.0.0-rc.6"
+ "@solidjs/web": "^2.0.0-rc.7"
},
"devDependencies": {
"@solidjs/web": "workspace:*",
diff --git a/packages/signals/CHANGELOG.md b/packages/signals/CHANGELOG.md
index cdf42bf7f..2ccf72bdc 100644
--- a/packages/signals/CHANGELOG.md
+++ b/packages/signals/CHANGELOG.md
@@ -1,5 +1,118 @@
# @solidjs/signals
+## 2.0.0-rc.7
+
+### Patch Changes
+
+- 215de3b: Align store overloads across the signals, client, and server entry points. Plain stores share `StoreOptions`, projection forms share `ProjectionOptions`, plain optimistic stores expose their existing options argument, and derived optimistic stores are typed as refreshable.
+- 6c8c956: Attribution: `feedback()` gains the fact tables that have no verdict of their own. `flights` counts, per async source, flights started, landed, and abandoned (superseded by a newer flight before landing — the re-ask-on-every-keystroke signature) with landed wall time. `fallbacks` measures, per loading boundary (named by owner path), how many times and for how long its fallback was shown and how many shows were sub-150ms flashes — the other end of the SILENT_HOLD spectrum. `sources` rows gain `late`/`lateMs`: acknowledged holds that still ran past `holds.infoMs`, where the affordance is not the whole answer. New `AttributionHooks.boundaryFallback` hook point at the boundary's source-set transitions. `@solidjs/diagnostics` exports the `FlightStats` and `FallbackStats` types; artifacts and the bridge carry the new tables through the existing `feedback` field.
+- 1a1e2f2: Attribution: `feedback()` — what the user waited on, as ranked tables.
+
+ `DEV.attribution.feedback()` is a pure fold over the records the engine already keeps — `holds()` and the interaction on each re-run — with no measurement or hook sites of its own, the way `costs()` folds re-runs into scope and write tables. `sources` ranks each set of async sources that held writes by the silent time spent behind them, with `holds`/`heldMs`/`worstMs`, `silent`/`silentMs`, `acknowledgedBy` (which affordance answered and in how many holds — a source acknowledged on one screen and silent on another reads as exactly that), `latestOnly` (answered only by a `latest()` shadow), the `interactions` that were held, the distinct `writes`, and `actions`. `interactions` ranks user events (type + target; repeated dispatches fold together) by total cost, pairing the synchronous re-run work one dispatch caused (`runs`, `selfMs`, `worstDispatchMs` — the long-flush hazard) with the time its writes spent held (`holds`, `heldMs`, `silentMs`, `worstHoldMs` — the silent-hold hazard): the two INP failure modes as columns of one row. Every hold counts at any duration; `SILENT_HOLD` remains the thresholded verdict over the same records. New exported types `FeedbackSource` and `FeedbackInteraction`; the reactivity-diagnostics skill gains a "where to start" entry, and `solid-js`'s console footer names the surface.
+
+- 7c14e23: Attribution: write provenance — who performed a change.
+
+ Every root `ChangeRecord` now carries `origin`: the imperative frame that made the write. `interaction` (a user event — type, described target such as `button#next "Next →"`, and dispatch time), `effect` (the callback's name), `action` (the generator's name), `async` (the landing's node), or `external` (timers, sockets, promise callbacks — including writes after an `await` rather than a `yield` inside an action, the documented transaction escape). Frames nested under an interaction carry it: an action a click started (every step, including post-`yield` resumptions), an effect whose run a click's write caused, an async flight a click's write launched. Why-chains print the origin after the write; `RerunEvent.interaction` and `HoldEvent.interaction` expose the interaction a run or hold traces back to, and `SILENT_HOLD` now opens with what the user did and measures the wait from the event, not from the first parked flush.
+
+ `@solidjs/web` declares the interaction around its two dispatch sites — delegated events (`onClick`, `onInput`, `onKeyDown`, pointer events: every INP-relevant type) and runtime-attached direct handlers (spreads, non-literal handler expressions) — via the new `DEV.attribution.withInteraction(ref, fn)`, which custom renderers and test harnesses can call themselves. New core dev hooks `effectRunStart`/`effectRunEnd` (replacing `effectRun`) and `actionStepStart`/`actionStepEnd`; all sites fold out of prod, verified byte-identical against the size scenarios.
+
+- ef2b02c: Internal cleanup with no behavior change: inline four single-use helpers (`hasContext`/`isUndefined`, `markCovered`, `shallowWithSymbols`), delete two dead ones (`isNextProxy`, `ownEnumerableKeysPlain`), and collapse `spread()`'s nullish-source handling into one accessor closure. A few dozen bytes off the app scenarios.
+- c6c415b: Mark `createTrackedEffect` as `@deprecated`. It is retained to ease 1.x migration, but it should not appear in new code: use `createEffect(compute, effect)` for side effects that follow reactive state (it separates tracking from the side effect, knows its dependencies before it runs, and participates in async and transitions) and `onSettled` for one-time DOM work after render. `onSettled` is unaffected (it uses the internal tracked-effect node directly).
+- 6c8c956: Diagnostics console addressability: compiled JSX binding effects (attribute, class, style, property, spread, insert) are tagged in dev with the element they write, and a console diagnostic about such an effect prints that element as a second argument — hover highlights it on the page, click jumps to it in the Elements panel. Why-chains (`DEV.attribution.enable()` logging) print as collapsed console groups, one headline per run with the causes inside. The once-per-code footer now pairs the installed skill path with the file's stable GitHub URL, anchored to the code's section.
+- d5aba4b: `WIDE_WRITE` and `HOT_SCOPE_FANOUT` diagnostics, and the reactivity-diagnostics and agent-loops skills, now prescribe a projection (`createProjection`, or a `createStore(fn)` keyed by id) as the fan-out repair. They previously named an API that is not part of 2.0 (#3304).
+- 3ae0ca0: Diagnostics locate themselves and report once.
+ - Every `DiagnosticEvent` now carries `ownerPath` — the root-first chain of named owners enclosing the subject (`["", "", "effect"]`). Component roots are labeled `` by `solid-js`'s dev component wrapper, so the path reads as the component tree down to the scope; owned-scope write errors in a component body now say `(in )`.
+ - Console reports are a single entry per finding: message, an `in › › effect` line, and the once-per-code repair footer as trailing lines — the footer no longer lands as a separate, duplicate-looking `[CODE]` line. Advisory (`info`) events emit no footer at all.
+ - `ASYNC_OUTSIDE_LOADING_BOUNDARY` fires once per `render()` instead of once per pending render effect (N async siblings at mount produced N copies).
+ - New dev-only helpers on the signals core: `reportDiagnostic(entry)` (the console face) and `ownerPath(subject)`; `emitDiagnostic` takes an optional subject (defaulting to the ambient reactive context).
+
+- ae46c92: Comment-only: update doc paths in source comments after relocating the signals internals docs (INTERNALS-\*, SPEC-ASYNC-SEMANTICS, rules-mining) from the package root into `packages/signals/docs/`. No behavior change.
+- 6c8c956: Attribution: `EFFECT_RELAY_TEAR` diagnostic — derived state kept in sync by an effect (`createEffect(() => f(a()), v => setS(v))`). "Should have been a memo" is a claim about intent the runtime cannot see; what it can see is the harm: every scope that reads both `a` and `S` runs twice for one write of `a` — once in the flush where `a` changed (against the stale `S`), once after the effect's write lands — and the first frame was inconsistent. The engine proves that from the cause chain (a re-run whose root writes all came from effects, one of whose runs shares a root write with the victim's previous run) and reports it once per relay, with intent heuristics as message modifiers rather than gates: `copy` (the written value is the effect's compute output — by contract a pure function of its tracked reads, so derivable; warns immediately, and on its own after two runs even with no double-running reader, since everything reading the copy paints a flush behind the source), `passthrough` (the compute output is one of the effect's sources — the prop-to-state port: read the source directly), and `soleWriter` (nothing else writes the signal). A tear whose write is none of these is `info` (a DOM-measurement effect tears legitimately — the cost of measuring) until the same relay has torn three times. The `reactivity-diagnostics` skill documents the code and repairs.
+- 1a1e2f2: Attribution: `EFFECT_WRITES_OWN_SOURCE` diagnostic. An effect whose callback writes a value its own inputs depend on converges (the second run finds nothing to change) rather than looping, so the flush guard never fires — yet the flush settled in two passes and the screen rendered the pre-write value in between. The engine now walks each effect re-run's cause chain (root writes, through any depth of memos) and, when a root write's effect origin resolves to the effect that is re-running, reports the cycle once: `warn` for a single effect (the written value is a function of what the effect reads — make it a memo, or normalize where the source is written), `info` for a cycle relayed across several effects (each effect-origin write is joined to the run that made it, so the walk continues hop by hop). Effect-origin `ChangeOrigin` frames gain `run`, the `RerunEvent.run` whose effect phase performed the write. The `reactivity-diagnostics` skill documents the code and repair.
+- f98bd77: Hold conditional reveals that first observe async work started in an earlier flush. The revealing signal now stays pending until the details can commit with it, including when the reveal creates a new child reader. Preserve fresh/reset loading-boundary fallbacks and avoid opening a second transition for readers already waiting in one.
+- fc7e626: Merge clone-path folds onto a container privatized mid-batch (#3271). Family and array drafts fold by swapping their pending backing in and re-slotting the parent with a CAS against the pre-batch old. When a descendant of the same node was written earlier in the draft, the descendant's fold path-copies THROUGH the ancestor first — privatizeCommitted clones the ancestor's committed backing and re-points the parent slot at the clone — so the ancestor's own fold swapped in a stale ensurePB-time clone, failed the parent CAS, and its writes were silently discarded (writable projections; plain object stores fold through the overlay path and were immune). Such folds now merge the batch's written keys onto the privatized container in place — the trap's written-keys bound is authoritative, with a value-diff fallback when an array length write poisoned it — composing both folds instead of losing one.
+- d50e855: rc.6 P1 store sweep — three fold-machinery gaps reported by @brenelz, all predating the #3271 fix:
+ - **#3282** — an array move (`reverse`/`unshift`/`splice`) plus an edit of a moved row corrupted sibling rows: the row target's parent-key is stamped at wrap time and never followed the move, so the fold's parent-slot re-point wrote the edited row's clone over whichever sibling now occupied the old index (`[1,2]` became `[1,1]`). Fold-time slot writes (privatization stitch, drainFolds path-copy, and the eager-fold twin) now resolve the slot by raw identity when the stamped key is stale — arrays only, fold-time only, no read-path cost.
+ - **#3283** — `deep()` silently unsubscribed from every untouched child after a parent-field edit: the walk bypasses the proxy traps, and a bare `Reflect.ownKeys` on a plain-object overlay pending backing (own keys = this batch's writes) hid inherited committed keys from the mid-flush re-walk, dropping those records from the effect's refreshed dependency set. The walk now merges committed keys minus deletes, mirroring the ownKeys trap's #3044 overlay merge.
+ - **#3284** — in derived stores, a descendant write disconnected ancestor observers and broke proxy identity: `privatizeCommitted` registered its clone only in the global lookup, but family targets resolve children through `fam.map`, so the next parent read wrapped a fresh target and orphaned the original's nodes. The clone now registers in the target's own map.
+
+- 8f9f369: Suspend uninitialized async values across optimistic lanes so `latest()`-conditioned branches wait for their first value instead of rendering `undefined`.
+- c531e2a: Fix `refresh()` of an optimistic value throwing `GlobalQueue._notifyAuthoritativeObservers is not a function` and halting the graph in apps that never call `until()` (#3303). The refresh waiter reads authoritatively — the override is not the answer it waits for — which marks the node as authoritatively observed; when the re-ask then landed equal to the override, the wakeup went through a late-bound hook only `until()` installed. `refresh()` now installs it too.
+- aed21ac: Fix a same-batch store reset leaving subscribers on the cancelled value (#3296). A draft write (`setStore(s => { s.count = 1 })`) notifies its node at setter exit; an adoption in the same batch (`setStore(reconcile(...))` or a returned replacement) discards the draft and diffs the incoming object against the committed backing, so a key the draft changed and the adoption restored never re-notified — untracked reads showed the reset while memos and effects committed the draft value. Adoptions now diff against the view the nodes were last told — the draft's pending backing when one preceded them — exactly as a second draft write would; the last write wins for every setter form.
+- b3c94be: Fix `createTrackedEffect` missing a signal written during a render-effect callback (#3291). Tracked effects read with committed visibility, but their wake was pushed straight into the user queue at notify time, so a write staged during a flush's render phase re-ran the effect in the same pass — before the value committed — and nothing re-notified it afterwards. Wakes (and the first run) now ride the heap like every other subscriber, so the run always lands after the commit; the tracked-effect special case in the scheduler is removed.
+- 0653673: Route errors thrown while applying asynchronous computed setters through the
+ node's error state. This prevents user callbacks invoked during asynchronous
+ projection reconciliation, such as key selectors, from escaping as unhandled
+ promise rejections.
+- 6c8c956: Attribution: `IMMUTABLE_UPDATE_IN_STORE` diagnostic. A store setter that replaces a container with a fresh object or array whose leaves are mostly the same values — `draft.user = { ...draft.user, name }`, `draft.items = [...draft.items, x]`, `draft.items = draft.items.filter(…)` — is the React habit the store does not need: it tracks leaves, so a fresh container makes every reader of the container's path re-run for the one leaf that moved. The store's write-channel notify now announces replaced containers to the attribution engine with a leaf census (identity on unwrapped values; object keys by key, array items by membership; containers over 64 leaves are skipped), and the engine warns once per store path when at least half the leaves carried over unchanged, naming the draft mutation that touches only the changed key or index and `reconcile()` for data arriving from outside. Genuinely new data (nothing carried over), draft mutation, and `reconcile()` do not report. New `AttributionHooks.storeReplaced` hook point.
+- 94fe5b4: Drop the lane source's redundant `isPending` companion refresh on derived pending/settle; the verdict never depended on it and the source's own paths keep it current.
+- 23477ae: Optimistic and `latest()` lanes now hold under the same rule as transactions: async derived from the lane holds the lane's reveal only when a render effect observes it pending and no `Loading` boundary catches it. An async memo nobody renders, or one inside a boundary that shows its fallback, no longer blocks the lane (#3289).
+- f4d3c87: Responsiveness thresholds and the LONG_HOLD diagnostic.
+ - `SILENT_HOLD` defaults tighten to `holds: { infoMs: 100, warnMs: 200 }` (from 300/500): RAIL's "feels instant" ceiling and the INP "good" ceiling. The engine measures to the commit, not the paint, so every number is a floor on what the user saw; the console's thresholds now sit at the strict end of the band.
+ - New `LONG_HOLD` (`responsiveness` kind): an acknowledged hold whose quiescent tail — from the last write to join it to the commit — reached `longHolds.infoMs` (default 500ms), `warn` from `longHolds.warnMs` (1000ms). Measured from the last join so a hold that keeps taking input is judged by each wait, not its lifetime. The repair is a fallback: a `Loading` boundary keyed with `on` (a revealed boundary without `on` keeps the old content — that is the hold), a fresh boundary, or making the data fast. A silent long hold stays one `SILENT_HOLD` with the same repair appended and `data.long: true`.
+ - `HoldEvent.tailMs` added; `holdMs` now runs from the interaction dispatch or the first parked flush, whichever is earlier (a node rewritten mid-hold keeps only its latest record, so the flush clock keeps the first wait from being forgotten). `ChangeRecord.at` stamps root writes.
+ - `feedback().sources[].late/lateMs` replaced by `long/longMs`: holds whose tail reached the long-hold threshold, acknowledged or not.
+ - `@solidjs/diagnostics` artifact format version 3 (`tailMs` on holds, `long`/`longMs` on sources); hold evidence in assertion failures includes `tailMs`.
+ - `RerunEvent.phase` value `"transition"` renamed to `"held"` (`"plain" | "held" | "optimistic"`) — the dev surface uses one word for the state.
+
+- 067e3bc: Optimistic increments keep stacking after a sibling landing. With several
+ optimistic-store actions in flight (optimistic `votes++`, server confirm,
+ `refresh(store)`), the first vote's truth landing is staged into the
+ transaction that still retains the second vote, and that vote's increment
+ replays over it. A third click's draft then read the staged truth WITHOUT
+ the replayed override: draft reads composed live overrides only while no
+ pending backing existed, and `votes++` reads before the first write triggers
+ the view-reseed hand-off. It read base, wrote base + 1, and its override
+ landed on the value already on screen — the click was invisible and the
+ count stuck (or fell back) until truth caught up. Draft reads now compose
+ overrides whenever the pending backing is not the draft's own view-seeded
+ clone.
+- 8a65e5e: An optimistic store's first flight suspends into its Loading boundary again.
+ The flight-owned transaction (#3146) was declared for the uninitialized
+ first ask too, so every transition-riding consumer — `render()`'s scheduled
+ root insert included — was held until the initial fetch landed: the page
+ stayed blank (content outside the boundary included) and the boundary's
+ fallback never showed, while `createStore(fn, seed)` and
+ `createOptimistic(fn, seed)` in the same spot showed it. Nothing has
+ committed on a first flight, so there is no truth to keep on screen and no
+ optimistic state to protect: it now declares nothing, like the loading
+ window (#2933). Refetch flights declare exactly as before, so bare
+ optimistic writes during an in-flight refetch still ride the flight's
+ transaction (#2951) and content stays put until the new truth lands.
+- f24e53d: `refresh()` after a held manual write stays a quiet re-ask. When an action
+ wrote to a derived store and later called `refresh(store)`, the lift of the
+ manual-write mask (#3026) dropped the re-ask classification, so the refetch
+ was treated as a brand-new question and pended every leaf — every sibling
+ row lit up `isPending`, and a row-scoped `affects()` could not narrow it.
+ The lift now keeps the classification: same-question motion stays silent,
+ and only the written slot and any declared `affects()` mark read pending
+ until the truth lands. Same-tick precedence (#2692) is unchanged.
+- fa568d3: Relocate #3277's uninitialized cross-lane suspension check from core `read()` into the optimistic module's `laneSuspends`. No behavior change — the check is only reachable under a lane, which implies the engine is installed — but the inline placement taxed every bundle including storeless floors (27-66 B across five size scenarios); in `laneSuspends` only bundles that retain the optimistic module pay.
+- d601119: Remove the experimental patch channel and patch-mode list driver (always opt-in, never default). Graph-native regions own value delivery and the unified-For design owns list structure, so the channel's parallel delivery machinery is retired: `patch.ts`/`patch-driver.ts` deleted, the compiler-contract exports (`registerPatch`/`registerRowOps`/`registerSlotPatch`/`patchableRaw`, `patchDriver`/`rowProof`/`driveList`) removed, the `patchDriver` compiler option dropped from both compilers, the insert `$ll` seam stripped, and the write-side channel struct dieted to the single written-keys bound (`t.wk`) the core fold/notify paths actually use. Store-family app bundles reclaim up to ~900 B brotli; every measured tier shrinks.
+- ac5159a: Preserve the supplied type in `Store` instead of adding a shallow readonly mapping.
+- de1c8b5: Revert the complete-seed requirement on derived store forms (#3258). Derived `createStore`, `createProjection`, and derived `createOptimisticStore` accept `Partial` seeds again, on maintainer review: requiring a full `T` forces callers to fabricate a throwaway complete object in the common async case — any object store reconciling on a non-`id` key needs the options slot, hence the seed slot — while the seed is never observable there (reads pend until the first resolution). The type-honesty concern it addressed is real only for sync draft-reading callbacks and is better served by the seedless-callback direction discussed in #3194. Since #3258 never shipped in a release, its pending changeset is dropped rather than superseded; the API is unchanged from 2.0.0-rc.6. The #3260 overload alignment (slot order, `shallow` in options, `Refreshable` derived returns) is unaffected.
+- 01e3a57: Attribution: transition holds and the `SILENT_HOLD` diagnostic.
+
+ When a write lands on async work the runtime holds it until the data settles — correct, but from the user's side the click did nothing until then. The attribution engine now records every such hold that staged a root write (`DEV.attribution.holds()`: duration, parked flushes, the held writes with their values, the async blockers, and which affordances answered it), and emits `SILENT_HOLD` when the screen provably rendered no acknowledgment: no `isPending()`/`latest()` reader anywhere downstream of the held writes or their blockers, no optimistic value, no `affects()` mark, and no effect ran inside the parked flushes. The verdict is tiered by `holds: { infoMs, warnMs }` (default 300/500ms): advisory on the structured channel, then a console `warn` naming the write, the blocker, and the concrete repair — `isPending(() => blocker())`, `latest(source)`, or `createOptimistic` for actions. Holds with no root write (initial loads, bare `refresh()`) are never judged.
+
+ New dev hook points on the core (`effectRun`, `holdStart`/`holdEnd`, `transitionSettled`, `transitionMerged`) sit outside every `try` and fold out of prod — verified byte-identical against the size scenarios. `DiagnosticKind` gains `"responsiveness"`; `solid-js`'s console footer teaches the attribution surface for it, and the reactivity-diagnostics skill documents the repair.
+
+- e346e61: Store leaf nodes ride `slotSignal` — one pre-shaped literal with `_host`/`_key` backrefs replacing the per-node options object, equals closure, unobserved closure, and the NodeExtension that held it; the unobserved sweep dispatches CONFIG_SLOT_NODE nodes to one shared hook. getNode self-time −23% on dbmon warm mounts.
+- 713a910: Internal: the store fold's descriptor-preserving property copy is one `copyOwn` helper instead of three inline repeats. No behavior change.
+- e346e61: Store first-read diet: the get trap's accessor probe verdict threads through to node creation (one descriptor scan per first read, not two), the trap's duplicate node-map lookup is hoisted, and the first tracked read populates the wrap cache so the second read skips wrapNext. dbmon mount min −4%, get-trap self-time −19%.
+- 0255729: Stop the store proxy's dev strict-read check from firing on the engine's
+ thenable probe. Resolving a promise with a store proxy — `refresh(store)`'s
+ waiter delivers the store, `Promise.resolve(store)`, `return store` from an
+ async function — makes the engine read `store.then` synchronously in the
+ caller's scope. When that scope carries a strict-read label (an effect
+ callback, a component body) the read produced a spurious
+ `STRICT_READ_UNTRACKED` warning, and against a refetching derived store it
+ could escalate to the `PENDING_ASYNC_UNTRACKED_READ` throw, rejecting the
+ promise being resolved. `await refresh(list)` inside an action logged the
+ warning on every call. The `then` probe is not a read the user wrote and is
+ now exempt from both.
+- 6c8c956: Attribution: `UNSTABLE_LIST_IDENTITY` diagnostic. When a `mapArray`/`` update disposes and recreates most rows while the entering items are field-for-field equivalent to the ones they replaced (a re-fetch handed back fresh objects for the same records under identity keying, or a key function returned unstable keys), every row's DOM and state was thrown away and rebuilt for data that did not change. `mapArray` now hands the exited and entered items to the attribution engine after a churning commit (`AttributionHooks.listChurn`); the engine pairs them (by `id`/`key`/`_id` when present, else by position), samples shallow equivalence, and warns once per list naming the repair — key by a stable field or merge with `reconcile(data, "id")`, or, when a key function is already in use, return a stable field from it. `mapArray` nodes now carry the `name` option as their node name in dev so the list is named in the report.
+
## 2.0.0-rc.6
### Patch Changes
diff --git a/packages/signals/package.json b/packages/signals/package.json
index f9dae40b0..12bdb13bb 100644
--- a/packages/signals/package.json
+++ b/packages/signals/package.json
@@ -1,6 +1,6 @@
{
"name": "@solidjs/signals",
- "version": "2.0.0-rc.6",
+ "version": "2.0.0-rc.7",
"description": "Solid's reactive primitives: signals, memos, effects, stores, and async-aware computations.",
"author": "Ryan Carniato",
"license": "MIT",
diff --git a/packages/solid/CHANGELOG.md b/packages/solid/CHANGELOG.md
index 6c9ae68bf..dda35b9f0 100644
--- a/packages/solid/CHANGELOG.md
+++ b/packages/solid/CHANGELOG.md
@@ -1,5 +1,102 @@
# solid-js
+## 2.0.0-rc.7
+
+### Patch Changes
+
+- 215de3b: Align store overloads across the signals, client, and server entry points. Plain stores share `StoreOptions`, projection forms share `ProjectionOptions`, plain optimistic stores expose their existing options argument, and derived optimistic stores are typed as refreshable.
+- 1a1e2f2: Attribution: `feedback()` — what the user waited on, as ranked tables.
+
+ `DEV.attribution.feedback()` is a pure fold over the records the engine already keeps — `holds()` and the interaction on each re-run — with no measurement or hook sites of its own, the way `costs()` folds re-runs into scope and write tables. `sources` ranks each set of async sources that held writes by the silent time spent behind them, with `holds`/`heldMs`/`worstMs`, `silent`/`silentMs`, `acknowledgedBy` (which affordance answered and in how many holds — a source acknowledged on one screen and silent on another reads as exactly that), `latestOnly` (answered only by a `latest()` shadow), the `interactions` that were held, the distinct `writes`, and `actions`. `interactions` ranks user events (type + target; repeated dispatches fold together) by total cost, pairing the synchronous re-run work one dispatch caused (`runs`, `selfMs`, `worstDispatchMs` — the long-flush hazard) with the time its writes spent held (`holds`, `heldMs`, `silentMs`, `worstHoldMs` — the silent-hold hazard): the two INP failure modes as columns of one row. Every hold counts at any duration; `SILENT_HOLD` remains the thresholded verdict over the same records. New exported types `FeedbackSource` and `FeedbackInteraction`; the reactivity-diagnostics skill gains a "where to start" entry, and `solid-js`'s console footer names the surface.
+
+- 7c14e23: Attribution: write provenance — who performed a change.
+
+ Every root `ChangeRecord` now carries `origin`: the imperative frame that made the write. `interaction` (a user event — type, described target such as `button#next "Next →"`, and dispatch time), `effect` (the callback's name), `action` (the generator's name), `async` (the landing's node), or `external` (timers, sockets, promise callbacks — including writes after an `await` rather than a `yield` inside an action, the documented transaction escape). Frames nested under an interaction carry it: an action a click started (every step, including post-`yield` resumptions), an effect whose run a click's write caused, an async flight a click's write launched. Why-chains print the origin after the write; `RerunEvent.interaction` and `HoldEvent.interaction` expose the interaction a run or hold traces back to, and `SILENT_HOLD` now opens with what the user did and measures the wait from the event, not from the first parked flush.
+
+ `@solidjs/web` declares the interaction around its two dispatch sites — delegated events (`onClick`, `onInput`, `onKeyDown`, pointer events: every INP-relevant type) and runtime-attached direct handlers (spreads, non-literal handler expressions) — via the new `DEV.attribution.withInteraction(ref, fn)`, which custom renderers and test harnesses can call themselves. New core dev hooks `effectRunStart`/`effectRunEnd` (replacing `effectRun`) and `actionStepStart`/`actionStepEnd`; all sites fold out of prod, verified byte-identical against the size scenarios.
+
+- c6c415b: Mark `createTrackedEffect` as `@deprecated`. It is retained to ease 1.x migration, but it should not appear in new code: use `createEffect(compute, effect)` for side effects that follow reactive state (it separates tracking from the side effect, knows its dependencies before it runs, and participates in async and transitions) and `onSettled` for one-time DOM work after render. `onSettled` is unaffected (it uses the internal tracked-effect node directly).
+- 6c8c956: Diagnostics console addressability: compiled JSX binding effects (attribute, class, style, property, spread, insert) are tagged in dev with the element they write, and a console diagnostic about such an effect prints that element as a second argument — hover highlights it on the page, click jumps to it in the Elements panel. Why-chains (`DEV.attribution.enable()` logging) print as collapsed console groups, one headline per run with the causes inside. The once-per-code footer now pairs the installed skill path with the file's stable GitHub URL, anchored to the code's section.
+- d5aba4b: `WIDE_WRITE` and `HOT_SCOPE_FANOUT` diagnostics, and the reactivity-diagnostics and agent-loops skills, now prescribe a projection (`createProjection`, or a `createStore(fn)` keyed by id) as the fan-out repair. They previously named an API that is not part of 2.0 (#3304).
+- 3ae0ca0: Diagnostics locate themselves and report once.
+ - Every `DiagnosticEvent` now carries `ownerPath` — the root-first chain of named owners enclosing the subject (`["", "", "effect"]`). Component roots are labeled `` by `solid-js`'s dev component wrapper, so the path reads as the component tree down to the scope; owned-scope write errors in a component body now say `(in )`.
+ - Console reports are a single entry per finding: message, an `in › › effect` line, and the once-per-code repair footer as trailing lines — the footer no longer lands as a separate, duplicate-looking `[CODE]` line. Advisory (`info`) events emit no footer at all.
+ - `ASYNC_OUTSIDE_LOADING_BOUNDARY` fires once per `render()` instead of once per pending render effect (N async siblings at mount produced N copies).
+ - New dev-only helpers on the signals core: `reportDiagnostic(entry)` (the console face) and `ownerPath(subject)`; `emitDiagnostic` takes an optional subject (defaulting to the ambient reactive context).
+
+- 1a1e2f2: Diagnostics: the responsiveness gate — holds and feedback in the artifact, `expectNoSilentHolds`, and Loop 4.
+
+ The artifact (format v2) now carries `attribution.holds` — every transition hold the scenario caused, with the held writes, the blockers, the wait measured from the interaction, and which affordances acknowledged it — and `attribution.feedback`, the ranked `sources`/`interactions` tables folded from them. JSONL egress emits `hold` and `feedback` records; the browser bridge and the `/__solid/diagnostics` protocol gain `holds()` and `feedback()` live queries.
+
+ New gates: `expectNoSilentHolds(artifact, { maxSilentMs })` fails on any hold the screen never acknowledged (no `isPending()`/`latest()` reader, no optimistic value, no `affects()` mark, nothing painted) with the interaction, held write, blocker, and duration as evidence; `expectHoldBudget(artifact, ms, { source })` bounds hold latency regardless of acknowledgment. `ScenarioBudget` gains `maxSilentHoldMs` and `maxHoldMs`; Vitest gains `toHaveNoSilentHolds()` and `toStayWithinHoldBudget(ms)`. The agent-loops skill gains "Loop 4 — Responsiveness": read `feedback.sources` first, repair by shape (`isPending` → `latest` → `createOptimistic`), and the explicit anti-repair — never make the gate pass by moving the write off the async path. Types `HoldEvent`, `ChangeOrigin`, `AttributionFeedback`, `FeedbackSource`, `FeedbackInteraction` are exported.
+
+- 6c8c956: Attribution: `EFFECT_RELAY_TEAR` diagnostic — derived state kept in sync by an effect (`createEffect(() => f(a()), v => setS(v))`). "Should have been a memo" is a claim about intent the runtime cannot see; what it can see is the harm: every scope that reads both `a` and `S` runs twice for one write of `a` — once in the flush where `a` changed (against the stale `S`), once after the effect's write lands — and the first frame was inconsistent. The engine proves that from the cause chain (a re-run whose root writes all came from effects, one of whose runs shares a root write with the victim's previous run) and reports it once per relay, with intent heuristics as message modifiers rather than gates: `copy` (the written value is the effect's compute output — by contract a pure function of its tracked reads, so derivable; warns immediately, and on its own after two runs even with no double-running reader, since everything reading the copy paints a flush behind the source), `passthrough` (the compute output is one of the effect's sources — the prop-to-state port: read the source directly), and `soleWriter` (nothing else writes the signal). A tear whose write is none of these is `info` (a DOM-measurement effect tears legitimately — the cost of measuring) until the same relay has torn three times. The `reactivity-diagnostics` skill documents the code and repairs.
+- 1a1e2f2: Attribution: `EFFECT_WRITES_OWN_SOURCE` diagnostic. An effect whose callback writes a value its own inputs depend on converges (the second run finds nothing to change) rather than looping, so the flush guard never fires — yet the flush settled in two passes and the screen rendered the pre-write value in between. The engine now walks each effect re-run's cause chain (root writes, through any depth of memos) and, when a root write's effect origin resolves to the effect that is re-running, reports the cycle once: `warn` for a single effect (the written value is a function of what the effect reads — make it a memo, or normalize where the source is written), `info` for a cycle relayed across several effects (each effect-origin write is joined to the run that made it, so the walk continues hop by hop). Effect-origin `ChangeOrigin` frames gain `run`, the `RerunEvent.run` whose effect phase performed the write. The `reactivity-diagnostics` skill documents the code and repair.
+- b6a90f9: Fix `deferStream` being a silent no-op inside a code-split `lazy()` component (#3299). A module load is code, not data: the shell's "no new async discovered during the sync render" rule cannot be evaluated for a segment whose code has not run, so the shell now waits for the chunk even under a `` (the boundary still owns the data the loaded code discovers — plain async streams behind the fallback as before, and a `deferStream` read inside the chunk holds the shell exactly like one in an eagerly imported component). Only the first render that reaches an un-preloaded chunk pays; a lazy mounted by a post-shell fragment streams as before.
+
+ Also closes a gap in the flush loop where a shell blocker registered while a boundary resumed during the drain — after the awaited set had settled but before the flush attempt snapshotted it — was never re-awaited.
+
+ `dynamic()` keeps streaming its source by default (a source is data of unknown cost) and gains a `deferStream` option to opt into holding the shell on it, with the same meaning as `createMemo`'s.
+
+- 3424f9a: Consume the first value of live-derived stores during SSR and reconnect their live sources after hydration.
+- 6c8c956: Attribution: `IMMUTABLE_UPDATE_IN_STORE` diagnostic. A store setter that replaces a container with a fresh object or array whose leaves are mostly the same values — `draft.user = { ...draft.user, name }`, `draft.items = [...draft.items, x]`, `draft.items = draft.items.filter(…)` — is the React habit the store does not need: it tracks leaves, so a fresh container makes every reader of the container's path re-run for the one leaf that moved. The store's write-channel notify now announces replaced containers to the attribution engine with a leaf census (identity on unwrapped values; object keys by key, array items by membership; containers over 64 leaves are skipped), and the engine warns once per store path when at least half the leaves carried over unchanged, naming the draft mutation that touches only the changed key or index and `reconcile()` for data arriving from outside. Genuinely new data (nothing carried over), draft mutation, and `reconcile()` do not report. New `AttributionHooks.storeReplaced` hook point.
+- f4d3c87: Responsiveness thresholds and the LONG_HOLD diagnostic.
+ - `SILENT_HOLD` defaults tighten to `holds: { infoMs: 100, warnMs: 200 }` (from 300/500): RAIL's "feels instant" ceiling and the INP "good" ceiling. The engine measures to the commit, not the paint, so every number is a floor on what the user saw; the console's thresholds now sit at the strict end of the band.
+ - New `LONG_HOLD` (`responsiveness` kind): an acknowledged hold whose quiescent tail — from the last write to join it to the commit — reached `longHolds.infoMs` (default 500ms), `warn` from `longHolds.warnMs` (1000ms). Measured from the last join so a hold that keeps taking input is judged by each wait, not its lifetime. The repair is a fallback: a `Loading` boundary keyed with `on` (a revealed boundary without `on` keeps the old content — that is the hold), a fresh boundary, or making the data fast. A silent long hold stays one `SILENT_HOLD` with the same repair appended and `data.long: true`.
+ - `HoldEvent.tailMs` added; `holdMs` now runs from the interaction dispatch or the first parked flush, whichever is earlier (a node rewritten mid-hold keeps only its latest record, so the flush clock keeps the first wait from being forgotten). `ChangeRecord.at` stamps root writes.
+ - `feedback().sources[].late/lateMs` replaced by `long/longMs`: holds whose tail reached the long-hold threshold, acknowledged or not.
+ - `@solidjs/diagnostics` artifact format version 3 (`tailMs` on holds, `long`/`longMs` on sources); hold evidence in assertion failures includes `tailMs`.
+ - `RerunEvent.phase` value `"transition"` renamed to `"held"` (`"plain" | "held" | "optimistic"`) — the dev surface uses one word for the state.
+
+- d601119: Remove the experimental patch channel and patch-mode list driver (always opt-in, never default). Graph-native regions own value delivery and the unified-For design owns list structure, so the channel's parallel delivery machinery is retired: `patch.ts`/`patch-driver.ts` deleted, the compiler-contract exports (`registerPatch`/`registerRowOps`/`registerSlotPatch`/`patchableRaw`, `patchDriver`/`rowProof`/`driveList`) removed, the `patchDriver` compiler option dropped from both compilers, the insert `$ll` seam stripped, and the write-side channel struct dieted to the single written-keys bound (`t.wk`) the core fold/notify paths actually use. Store-family app bundles reclaim up to ~900 B brotli; every measured tier shrinks.
+- ac5159a: Preserve the supplied type in `Store` instead of adding a shallow readonly mapping.
+- de1c8b5: Revert the complete-seed requirement on derived store forms (#3258). Derived `createStore`, `createProjection`, and derived `createOptimisticStore` accept `Partial` seeds again, on maintainer review: requiring a full `T` forces callers to fabricate a throwaway complete object in the common async case — any object store reconciling on a non-`id` key needs the options slot, hence the seed slot — while the seed is never observable there (reads pend until the first resolution). The type-honesty concern it addressed is real only for sync draft-reading callbacks and is better served by the seedless-callback direction discussed in #3194. Since #3258 never shipped in a release, its pending changeset is dropped rather than superseded; the API is unchanged from 2.0.0-rc.6. The #3260 overload alignment (slot order, `shallow` in options, `Refreshable` derived returns) is unaffected.
+- 01e3a57: Attribution: transition holds and the `SILENT_HOLD` diagnostic.
+
+ When a write lands on async work the runtime holds it until the data settles — correct, but from the user's side the click did nothing until then. The attribution engine now records every such hold that staged a root write (`DEV.attribution.holds()`: duration, parked flushes, the held writes with their values, the async blockers, and which affordances answered it), and emits `SILENT_HOLD` when the screen provably rendered no acknowledgment: no `isPending()`/`latest()` reader anywhere downstream of the held writes or their blockers, no optimistic value, no `affects()` mark, and no effect ran inside the parked flushes. The verdict is tiered by `holds: { infoMs, warnMs }` (default 300/500ms): advisory on the structured channel, then a console `warn` naming the write, the blocker, and the concrete repair — `isPending(() => blocker())`, `latest(source)`, or `createOptimistic` for actions. Holds with no root write (initial loads, bare `refresh()`) are never judged.
+
+ New dev hook points on the core (`effectRun`, `holdStart`/`holdEnd`, `transitionSettled`, `transitionMerged`) sit outside every `try` and fold out of prod — verified byte-identical against the size scenarios. `DiagnosticKind` gains `"responsiveness"`; `solid-js`'s console footer teaches the attribution surface for it, and the reactivity-diagnostics skill documents the repair.
+
+- 6c8c956: Attribution: `UNSTABLE_LIST_IDENTITY` diagnostic. When a `mapArray`/`` update disposes and recreates most rows while the entering items are field-for-field equivalent to the ones they replaced (a re-fetch handed back fresh objects for the same records under identity keying, or a key function returned unstable keys), every row's DOM and state was thrown away and rebuilt for data that did not change. `mapArray` now hands the exited and entered items to the attribution engine after a churning commit (`AttributionHooks.listChurn`); the engine pairs them (by `id`/`key`/`_id` when present, else by position), samples shallow equivalence, and warns once per list naming the repair — key by a stable field or merge with `reconcile(data, "id")`, or, when a key function is already in use, return a stable field from it. `mapArray` nodes now carry the `name` option as their node name in dev so the list is named in the report.
+- Updated dependencies [215de3b]
+- Updated dependencies [6c8c956]
+- Updated dependencies [1a1e2f2]
+- Updated dependencies [7c14e23]
+- Updated dependencies [ef2b02c]
+- Updated dependencies [c6c415b]
+- Updated dependencies [6c8c956]
+- Updated dependencies [d5aba4b]
+- Updated dependencies [3ae0ca0]
+- Updated dependencies [ae46c92]
+- Updated dependencies [6c8c956]
+- Updated dependencies [1a1e2f2]
+- Updated dependencies [f98bd77]
+- Updated dependencies [fc7e626]
+- Updated dependencies [d50e855]
+- Updated dependencies [8f9f369]
+- Updated dependencies [c531e2a]
+- Updated dependencies [aed21ac]
+- Updated dependencies [b3c94be]
+- Updated dependencies [0653673]
+- Updated dependencies [6c8c956]
+- Updated dependencies [94fe5b4]
+- Updated dependencies [23477ae]
+- Updated dependencies [f4d3c87]
+- Updated dependencies [067e3bc]
+- Updated dependencies [8a65e5e]
+- Updated dependencies [f24e53d]
+- Updated dependencies [fa568d3]
+- Updated dependencies [d601119]
+- Updated dependencies [ac5159a]
+- Updated dependencies [de1c8b5]
+- Updated dependencies [01e3a57]
+- Updated dependencies [e346e61]
+- Updated dependencies [713a910]
+- Updated dependencies [e346e61]
+- Updated dependencies [0255729]
+- Updated dependencies [6c8c956]
+ - @solidjs/signals@2.0.0-rc.7
+
## 2.0.0-rc.6
### Patch Changes
diff --git a/packages/solid/package.json b/packages/solid/package.json
index a71b59cd0..9f6a93e1a 100644
--- a/packages/solid/package.json
+++ b/packages/solid/package.json
@@ -1,7 +1,7 @@
{
"name": "solid-js",
"description": "Reactive JavaScript library for building user interfaces. Compiles JSX to real DOM with fine-grained signal-based updates — no virtual DOM.",
- "version": "2.0.0-rc.6",
+ "version": "2.0.0-rc.7",
"author": "Ryan Carniato",
"license": "MIT",
"homepage": "https://solidjs.com",
@@ -140,7 +140,7 @@
"performance"
],
"dependencies": {
- "@solidjs/signals": "^2.0.0-rc.6",
+ "@solidjs/signals": "^2.0.0-rc.7",
"csstype": "^3.1.0",
"seroval": "~1.5.4",
"seroval-plugins": "~1.5.4"
diff --git a/packages/test-integration/CHANGELOG.md b/packages/test-integration/CHANGELOG.md
index 73fa2f53a..9739f08d5 100644
--- a/packages/test-integration/CHANGELOG.md
+++ b/packages/test-integration/CHANGELOG.md
@@ -1,5 +1,85 @@
# test-integration
+## 2.0.0-rc.7
+
+### Patch Changes
+
+- Updated dependencies [ead7b1a]
+- Updated dependencies [215de3b]
+- Updated dependencies [6c8c956]
+- Updated dependencies [1a1e2f2]
+- Updated dependencies [7c14e23]
+- Updated dependencies [1226381]
+- Updated dependencies [7009adf]
+- Updated dependencies [ef2b02c]
+- Updated dependencies [a14c138]
+- Updated dependencies [c6c415b]
+- Updated dependencies [6c8c956]
+- Updated dependencies [d5aba4b]
+- Updated dependencies [3ae0ca0]
+- Updated dependencies [1a1e2f2]
+- Updated dependencies [ae46c92]
+- Updated dependencies [c08e974]
+- Updated dependencies [6c8c956]
+- Updated dependencies [1a1e2f2]
+- Updated dependencies [fbe5bef]
+- Updated dependencies [292bdc5]
+- Updated dependencies [f98bd77]
+- Updated dependencies [285a717]
+- Updated dependencies [8f11ea7]
+- Updated dependencies [d0ca3a4]
+- Updated dependencies [fc7e626]
+- Updated dependencies [d50e855]
+- Updated dependencies [8f9f369]
+- Updated dependencies [b6a90f9]
+- Updated dependencies [3424f9a]
+- Updated dependencies [6164667]
+- Updated dependencies [c531e2a]
+- Updated dependencies [b64071c]
+- Updated dependencies [aed21ac]
+- Updated dependencies [b3c94be]
+- Updated dependencies [b3586e8]
+- Updated dependencies [f21e060]
+- Updated dependencies [ecdfc3a]
+- Updated dependencies [5cee0f7]
+- Updated dependencies [0653673]
+- Updated dependencies [6c8c956]
+- Updated dependencies [6bb51c9]
+- Updated dependencies [94fe5b4]
+- Updated dependencies [23477ae]
+- Updated dependencies [f4d3c87]
+- Updated dependencies [067e3bc]
+- Updated dependencies [8a65e5e]
+- Updated dependencies [ff2ecf1]
+- Updated dependencies [c0bc9ba]
+- Updated dependencies [ace227e]
+- Updated dependencies [b7b17ab]
+- Updated dependencies [f24e53d]
+- Updated dependencies [ed6b605]
+- Updated dependencies [6c9f8f4]
+- Updated dependencies [3393fb6]
+- Updated dependencies [fa568d3]
+- Updated dependencies [d601119]
+- Updated dependencies [ac5159a]
+- Updated dependencies [ace227e]
+- Updated dependencies [de1c8b5]
+- Updated dependencies [d2b50e9]
+- Updated dependencies [84a94bc]
+- Updated dependencies [01e3a57]
+- Updated dependencies [e346e61]
+- Updated dependencies [713a910]
+- Updated dependencies [e346e61]
+- Updated dependencies [0255729]
+- Updated dependencies [a1ff286]
+- Updated dependencies [6c8c956]
+ - @solidjs/babel-plugin@2.0.0-rc.7
+ - @solidjs/signals@2.0.0-rc.7
+ - solid-js@2.0.0-rc.7
+ - @solidjs/web@2.0.0-rc.7
+ - @solidjs/universal@2.0.0-rc.7
+ - @solidjs/h@2.0.0-rc.7
+ - @solidjs/html@2.0.0-rc.7
+
## 2.0.0-rc.6
### Patch Changes
diff --git a/packages/test-integration/package.json b/packages/test-integration/package.json
index 12df9a453..4a76ad58f 100644
--- a/packages/test-integration/package.json
+++ b/packages/test-integration/package.json
@@ -21,5 +21,5 @@
"gitly": "^2.2.1",
"shelljs": "^0.8.5"
},
- "version": "2.0.0-rc.6"
+ "version": "2.0.0-rc.7"
}
diff --git a/packages/universal/CHANGELOG.md b/packages/universal/CHANGELOG.md
index 3dd9574a8..382def955 100644
--- a/packages/universal/CHANGELOG.md
+++ b/packages/universal/CHANGELOG.md
@@ -1,5 +1,31 @@
# @solidjs/universal
+## 2.0.0-rc.7
+
+### Patch Changes
+
+- d601119: Remove the experimental patch channel and patch-mode list driver (always opt-in, never default). Graph-native regions own value delivery and the unified-For design owns list structure, so the channel's parallel delivery machinery is retired: `patch.ts`/`patch-driver.ts` deleted, the compiler-contract exports (`registerPatch`/`registerRowOps`/`registerSlotPatch`/`patchableRaw`, `patchDriver`/`rowProof`/`driveList`) removed, the `patchDriver` compiler option dropped from both compilers, the insert `$ll` seam stripped, and the write-side channel struct dieted to the single written-keys bound (`t.wk`) the core fold/notify paths actually use. Store-family app bundles reclaim up to ~900 B brotli; every measured tier shrinks.
+- Updated dependencies [215de3b]
+- Updated dependencies [1a1e2f2]
+- Updated dependencies [7c14e23]
+- Updated dependencies [c6c415b]
+- Updated dependencies [6c8c956]
+- Updated dependencies [d5aba4b]
+- Updated dependencies [3ae0ca0]
+- Updated dependencies [1a1e2f2]
+- Updated dependencies [6c8c956]
+- Updated dependencies [1a1e2f2]
+- Updated dependencies [b6a90f9]
+- Updated dependencies [3424f9a]
+- Updated dependencies [6c8c956]
+- Updated dependencies [f4d3c87]
+- Updated dependencies [d601119]
+- Updated dependencies [ac5159a]
+- Updated dependencies [de1c8b5]
+- Updated dependencies [01e3a57]
+- Updated dependencies [6c8c956]
+ - solid-js@2.0.0-rc.7
+
## 2.0.0-rc.6
### Patch Changes
diff --git a/packages/universal/package.json b/packages/universal/package.json
index f7ccdfcc0..d1a6c1832 100644
--- a/packages/universal/package.json
+++ b/packages/universal/package.json
@@ -1,7 +1,7 @@
{
"name": "@solidjs/universal",
"description": "Solid's universal runtime for creating custom renderers",
- "version": "2.0.0-rc.6",
+ "version": "2.0.0-rc.7",
"author": "Ryan Carniato",
"license": "MIT",
"homepage": "https://solidjs.com",
@@ -58,7 +58,7 @@
"test": "vitest run"
},
"peerDependencies": {
- "solid-js": "^2.0.0-rc.6"
+ "solid-js": "^2.0.0-rc.7"
},
"devDependencies": {
"solid-js": "workspace:*"
diff --git a/packages/web/CHANGELOG.md b/packages/web/CHANGELOG.md
index 382a9084e..afb60f94c 100644
--- a/packages/web/CHANGELOG.md
+++ b/packages/web/CHANGELOG.md
@@ -1,5 +1,88 @@
# @solidjs/web
+## 2.0.0-rc.7
+
+### Patch Changes
+
+- 7c14e23: Attribution: write provenance — who performed a change.
+
+ Every root `ChangeRecord` now carries `origin`: the imperative frame that made the write. `interaction` (a user event — type, described target such as `button#next "Next →"`, and dispatch time), `effect` (the callback's name), `action` (the generator's name), `async` (the landing's node), or `external` (timers, sockets, promise callbacks — including writes after an `await` rather than a `yield` inside an action, the documented transaction escape). Frames nested under an interaction carry it: an action a click started (every step, including post-`yield` resumptions), an effect whose run a click's write caused, an async flight a click's write launched. Why-chains print the origin after the write; `RerunEvent.interaction` and `HoldEvent.interaction` expose the interaction a run or hold traces back to, and `SILENT_HOLD` now opens with what the user did and measures the wait from the event, not from the first parked flush.
+
+ `@solidjs/web` declares the interaction around its two dispatch sites — delegated events (`onClick`, `onInput`, `onKeyDown`, pointer events: every INP-relevant type) and runtime-attached direct handlers (spreads, non-literal handler expressions) — via the new `DEV.attribution.withInteraction(ref, fn)`, which custom renderers and test harnesses can call themselves. New core dev hooks `effectRunStart`/`effectRunEnd` (replacing `effectRun`) and `actionStepStart`/`actionStepEnd`; all sites fold out of prod, verified byte-identical against the size scenarios.
+
+- 1226381: Bind `GET()` grants to the function identity they were declared about (#3237). The grant — GET/HEAD dispatch plus the CSRF origin-gate exemption — was keyed by id alone, so `register -> register -> GET(oldReference)` handed the NEW function cross-site GET execution on the strength of a declaration the old one signed. The grant now records the declared function, and a single `declaresRead(id)` check governs both dispatch and the 405 `Allow` advertisement; a stale or unverifiable declaration fails closed (GET refused, POST + origin gate required). A declaration or `withMeta({ method })` write that would change an existing grant's binding throws in dev and fails closed in prod, never silently rebinds.
+- 7009adf: Bound the request body cap by the bytes actually received: a conforming Content-Length under the limit no longer skips the counting read, so an under-declared body cannot stream past `bodySizeLimit` into the decoder, and the abort/teardown coupling for abandoned uploads now covers declared-length POSTs too (#3236).
+- ef2b02c: Internal cleanup with no behavior change: inline four single-use helpers (`hasContext`/`isUndefined`, `markCovered`, `shallowWithSymbols`), delete two dead ones (`isNextProxy`, `ownEnumerableKeysPlain`), and collapse `spread()`'s nullish-source handling into one accessor closure. A few dozen bytes off the app scenarios.
+- a14c138: Dedupe the requested single-flight source ids to a first-seen-order set at entry, so a repeated id runs its collector once and echoes once in the response header instead of multiplying work by the caller-controlled list length (#3251)
+- 6c8c956: Diagnostics console addressability: compiled JSX binding effects (attribute, class, style, property, spread, insert) are tagged in dev with the element they write, and a console diagnostic about such an effect prints that element as a second argument — hover highlights it on the page, click jumps to it in the Elements panel. Why-chains (`DEV.attribution.enable()` logging) print as collapsed console groups, one headline per run with the causes inside. The once-per-code footer now pairs the installed skill path with the file's stable GitHub URL, anchored to the code's section.
+- c08e974: Document the per-handler `wrapInvocation` option as entry-only (#3240). Ruled: entry-only semantics are kept — the option wraps exactly the invocation the request addressed, and nested direct server-function calls made by the dispatched body are not re-wrapped by it; hop-by-hop policy belongs to the configured (ambient) hook, which wraps every direct call. TSDoc only, no runtime behavior change; the boundary is now pinned by a regression test.
+- fbe5bef: Encrypt the no-JS flash cookie (#3239). The flash carries the submitted form input — whatever the user typed — so its payload is now AES-GCM encrypted under a key derived (domain-separated) from the deployment secret: `configureServerFunctionsServer({ secret })`, falling back to the `globalThis.__SOLID_SECRET__` value the Solid bundler plugin injects into server builds. With no secret configured the outcome is withheld rather than sent in the clear (the post still redirects; dev builds warn once). Decryption failure — a tampered cookie, a rotated secret — reads as "no flash". The cookie now also carries `SameSite=Lax` and `Max-Age=60`, and `encodeFlashCookie`/`decodeFlashCookie` are async.
+- 292bdc5: Enforce provideEvent's exactly-once contract on direct SSR calls too: the invocation count #3172 added to HTTP dispatch now guards both legs through a shared `provideEventOnce` seam, so a hook that double-invokes or skips the callback fails loudly during a render instead of silently double-committing a mutation or answering `undefined`, while synchronous direct calls keep returning their value synchronously (#3246).
+- 285a717: Defuse the promise the decoder's abort sweep is about to reject (#3267). A `PromiseConstructor` node whose ref id collides with an already-assigned id (or is malformed) throws mid-registration, leaving a `{p, s, f}` deferred in the decoder's refs whose promise `ownDecodedPromises` never claimed — the deferred is not itself a Promise. The end-of-stream sweep then rejected that promise with no owner, and under Node's default policy one unauthenticated POST with a crafted argument body ended the process after the request was already refused 400. The sweep now takes ownership of `.p` before rejecting it, covering every promise it touches regardless of how the entry reached refs.
+- 8f11ea7: Guard failure channels under an Error carrier's non-enumerable own data slots (#3268). The #3235 guard walked Error carriers with `Object.keys`, but seroval encodes an Error's own properties through `getOwnPropertyNames` — so a rejected promise or erroring stream parked on a non-enumerable slot (`cause` is non-enumerable by spec since ES2022, and the ordinary place a wrapped driver error carries its context) was encoded without ever being walked: its failure reason rode the wire verbatim on a committed 200, and the rejection had no owner. The guard now descends an Error's own string-keyed data slots, enumerable or not. Hidden accessors remain the codec's read (47995412's pinned ruling): the walk still does not invoke what the author hid.
+- d0ca3a4: The no-JS flash cookie now records the UNBOUND function base as the submission's `url` — the request's pathname (`/`), never the query. A `.with()`-bound form's action url carries its bound arguments in `?args=…`, and integrations match submissions against the action's unbound base (the router's `s.url === fn.base`): a flash url wearing the binding stored, decoded, and then matched nothing on the post-redirect render. The seed now matches the scripted submission shape exactly — the base as `url`, bound arguments prepended to `input` (which the argument parser's `?args` prepend already provided).
+- b6a90f9: Fix `deferStream` being a silent no-op inside a code-split `lazy()` component (#3299). A module load is code, not data: the shell's "no new async discovered during the sync render" rule cannot be evaluated for a segment whose code has not run, so the shell now waits for the chunk even under a `` (the boundary still owns the data the loaded code discovers — plain async streams behind the fallback as before, and a `deferStream` read inside the chunk holds the shell exactly like one in an eagerly imported component). Only the first render that reaches an un-preloaded chunk pays; a lazy mounted by a post-shell fragment streams as before.
+
+ Also closes a gap in the flush loop where a shell blocker registered while a boundary resumed during the drain — after the awaited set had settled but before the flush attempt snapshotted it — was never re-awaited.
+
+ `dynamic()` keeps streaming its source by default (a source is data of unknown cost) and gains a `deferStream` option to opt into holding the shell on it, with the same meaning as `createMemo`'s.
+
+- 6164667: Fix a lone reactive spread crashing when its source is `null` or `undefined` (#3297). `` compiles to `spread(el, props)` with the accessor passed through, so an absent optional props object threw inside `spread()` (and `ssrElement` on the server) and halted the app's updates. A nullish source is now an empty spread: attributes applied by the previous value are removed and reactivity continues.
+- b64071c: Render textarea values supplied through SSR spreads as text content rather than invalid HTML attributes.
+- b3586e8: Validate document-shell templates in the document context (#3259). The `validate` pass round-trips templates through a body-context fragment parse, which strips ``/``/`` wrappers no matter how well-formed the markup — so once #3099 made validate failures compile errors, a root component owning the document shell failed to compile in plain client mode, and merely importing it (the jsdom component-test configuration) was fatal. Shell-rooted templates now parse as a document and the shell element is compared back — the analogue of the synthetic `
` wrap for table partials, in both the Babel plugin and the native compiler. Genuine restructuring (an implied ``, flow content in ``, a `
` split in `
`) still errors. Since `` parsing flattens shells, actually client-creating one now throws a descriptive dev-mode error from `template()` pointing at `hydrate()` — the failure moved from every import to the one broken act.
+- f21e060: Flash falsy no-JS outcomes (`0`, `false`, `""`, `null`) instead of silently dropping them: the flash decode and the no-JS handler now decide structurally (result presence, `Response` shape) rather than by truthiness, and dispatch no longer erodes a returned `null` to `undefined` on its way to the handler; an `undefined` outcome keeps its current no-cookie behavior pending ruling (#3248)
+- ecdfc3a: An awaited `renderToStream(...)` result now freezes the request's response head at completion — the render commits `event.response` right before its final dispose — so `httpStatus`/`httpHeader` declarations survive into `createSSRResponse(html, event)`, which sees the already-committed stub and passes it through. Previously the thenable disposed the render owner before resolving, while the head was still open, so every scope-tied declaration's cleanup retracted it: a page calling `httpStatus(404)` rendered through `await renderToStream(...)` came back as a 200 and its `httpHeader` writes vanished. The piped forms are unchanged (they already froze at shell flush), and so are the retraction semantics themselves — a scope disposed mid-render, such as an errored boundary that recovered, still retracts its declarations. Integrations no longer need to commit the stub from `onCompleteAll` to work around this.
+- 5cee0f7: Guard enumerable failure channels carried on Error results: the result-encoding guard walk now descends Error-prototyped carriers (which seroval encodes with their own properties) so a rejecting promise, erroring stream, or throwing iterable assigned onto a returned Error is sanitized and torn down like any other channel, while the carrier keeps its prototype, message, and own data (#3235).
+- 6bb51c9: Keep `-0` off the server-function JSON fast path. `JSON.stringify(-0)` is `"0"`, so a signed zero admitted by `isJSONSafe` rode the fast path and arrived as `+0` — a silent sign flip on the exact guard that already refuses `NaN` and the infinities for the same reason. `-0` now answers "not JSON-safe" and rides the codec, which spells it exactly, on both legs (argument lists and results).
+- ff2ecf1: Own every promise the server-function decoder mints. A rejected promise decoded out of a peer's payload — a rejection frame arriving mid-stream, or an atomic rejected-promise node settling synchronously during decode — had no owner when the consumer never read (or abandoned) the slot, and escaped as an unhandled rejection that ends a Node consumer under its default policy. The decoder now attaches a noop rejection handler at mint time, mirroring the encode side's `guardedPromise` ownership (#3216); real consumers still observe the rejection unchanged.
+- c0bc9ba: Adopt a `transformFlightResult` Response via the ownership seam (`ownResponse`) before stamping the mutation's cookies and accumulated headers onto it, so a Response the integration retains (a memoized shell) never accumulates one caller's session cookies and serves them to the next (#3234, completing #3155)
+- ace227e: Canonicalize resource identity qualifiers instead of comparing raw prop values, so two declarations of one request dedupe to one `` on both sides of hydration.
+
+ `false` now means absent, matching both attribute writers: `crossorigin={cond && "anonymous"}` no longer emits a second, byte-identical link when the condition is false.
+
+ `crossorigin` is compared by its CORS state rather than its spelling. It is a CORS settings attribute with three states — absent is No CORS, `use-credentials` (ASCII case-insensitive) is Use Credentials, and every other present value including `""`, a bare attribute and an invalid one is Anonymous — so the same font is no longer preloaded once per spelling, and the client adopts the server's link instead of mounting a second one for a request the browser already has.
+
+ Qualifier values are length-prefixed, so a value containing the identity delimiters can no longer collide with a different qualifier set and silently suppress another resource (`type: "a:media=b"` and `type: "a", media: "b"` were one identity).
+
+ Client-side adoption of a mount-once head resource now matches a server-emitted element on the full request identity rather than the href alone: two preloads sharing an href still differ if their destination, CORS mode, type, media or source set differ. The document client, the standalone frame client and the server all apply the same rules.
+
+- b7b17ab: Make `extractBody` own the stream it decodes: the body is read where it lies, never from an internal clone. An unread tee branch queues the whole payload in memory for the life of the read and defeats backpressure and cancellation — the same ownership defect fixed for the upload leg in `bufferBodyWithin` (#3217–#3219). `decodeResponse` keeps its documented contract (an integration's response stays readable) by cloning at its own entry — a branch that is then read in full; the client transport decodes the response it owns directly, and the server's argument road reuses its one deliberate clone (kept so `event.request` stays readable) for the empty-body inspection instead of teeing again. This is the clone half of #3244 only; connection teardown on completion is deliberately not included.
+- ed6b605: Throw a clear configuration error for invalid `wrapInvocation` values (#3238). A value other than a function or `undefined` — `null`, `false`, an options bag in the wrong slot — used to fail in the quietest available direction: falsy values silently took per-invocation policy (auth, logging) off the call, truthy non-functions threw a bare "not a function" mid-dispatch. The hook is now validated at the point it is resolved for an invocation, on both roads (HTTP dispatch and direct SSR calls), with an error naming `wrapInvocation` and the received type; `undefined` stays the one spelling of absence.
+- 6c9f8f4: Refuse an unrecognized `X-Server-Function-Format` tag before the decode switch runs. The content-type sniffing branches (there for untagged form posts) matched regardless of the tag, so a body tagged with a format this build has no case for — version skew from a newer peer, or a duplicated header that `Headers.get` joins into one unknown value — was silently reinterpreted as a form and the function ran on an argument it was never sent. Such bodies now answer 400 before dispatch, with a development message naming version skew; untagged bodies keep the sniffing, and an untagged empty body stays a zero-argument call (#3214).
+- 3393fb6: Refuse to flash when no storable cookie exists for a no-JS outcome (#3249). The degrade ladder (#3137) bounds the input echo and the result but never looked at `url` — pathname + search of a request the caller chose — so a long enough form action pushed the fully-degraded payload past the ~4 KB cookie ceiling and the encoder emitted a cookie the browser discards whole, with `truncated: true` inside asserting a degradation that never stored. `encodeFlashCookie` now returns `null` when even the degraded payload cannot fit, and the no-JS handler falls back to the plain redirect — never an oversized cookie, never a url truncated to a prefix that would attach the outcome to a submission it does not identify. Cookie naming, attributes, and refusal/redirect statuses are untouched (#3239, #3250 pending).
+- d601119: Remove the experimental patch channel and patch-mode list driver (always opt-in, never default). Graph-native regions own value delivery and the unified-For design owns list structure, so the channel's parallel delivery machinery is retired: `patch.ts`/`patch-driver.ts` deleted, the compiler-contract exports (`registerPatch`/`registerRowOps`/`registerSlotPatch`/`patchableRaw`, `patchDriver`/`rowProof`/`driveList`) removed, the `patchDriver` compiler option dropped from both compilers, the insert `$ll` seam stripped, and the write-side channel struct dieted to the single written-keys bound (`t.wk`) the core fold/notify paths actually use. Store-family app bundles reclaim up to ~900 B brotli; every measured tier shrinks.
+- ace227e: Support `imagesrcset` and `imagesizes` in typed image preloads, including the standard form without `href`. Candidate URLs inside `imagesrcset` must already be resolved by the integration.
+
+ The responsive pair is image-only. On any other destination the attribute is dropped and the link still ships — an integration that computes `imagesrcset` for every asset keeps its script and style preloads. An empty or non-string value counts as absent for the same reason, so a source set is never emitted as garbage the browser cannot parse. A descriptor whose only source was such a filtered attribute is dropped entirely rather than emitted as a `` with nothing to fetch.
+
+ `mountHeadResource` can adopt a source-set link: it has no href, so it matches a server-emitted link on a null href plus the identity qualifiers — the rule the frame client already applied.
+
+ Development builds warn when `imagesrcset` uses a width descriptor without `imagesizes` (the source size falls back to `100vw`, so the preload can miss the image the `` selects), and when a manifest source set carries a relative candidate — candidates are not joined with `_base`, so they resolve against the document URL whichever base the manifest declares. That check walks the source set the way the spec's parser does, so commas inside a candidate URL are not mistaken for candidate separators.
+
+- d2b50e9: Revert #3187's deferred element creation in `Dynamic`. String-component `Dynamic` is once again an element at component creation on the client — `spread` and `ref` callbacks run then, matching the hydration path — instead of a thunk materialized by the consuming `insert()`. The deferral moved ref writes into the render phase of the flush for `` inside ``, where `createTrackedEffect` readers never observed them (#3291: every Kobalte popper rendered unpositioned). The namespace of ambiguous tags (`a`, `script`, `style`, `title`) rendered through `Dynamic` inside SVG content is an accepted limitation, as in 1.x; use a static element for those.
+- 84a94bc: Scope deferred work nested inside plain-object and array carriers to the producing call's request event (#3241, completing #3222). The HTTP road already applied the wrapping in the encoded representation (the guard walk's rebuilt shells); the direct SSR road only looked at the returned value itself, so `return { rows: cursor() }` ran its generator under the render's ambient event — two concurrent direct calls read and wrote each other's `locals`, and the render's own. The direct road now descends plain-object/array carriers and hands the caller a shallow-rebuilt carrier with the bound wrappers in the deferred slots; the user's returned containers are never written into, and results with nothing deferred keep their identity. Set/Map members, class instances, and frozen/non-writable slots are deliberately out of the carrier set (pinned by test): bodies reached through them stay bound to nothing, as before.
+- a1ff286: Run `transformResult` for plain thrown errors as its documentation already promises: the hook now runs once at the thrown path's entry (`context.thrown` set) for every thrown value — not only thrown Response/envelope shapes — and the response tail is selected from its output, while the wire stays sanitized and a hook that itself throws is contained as a sanitized 500 (#3247).
+- Updated dependencies [215de3b]
+- Updated dependencies [1a1e2f2]
+- Updated dependencies [7c14e23]
+- Updated dependencies [c6c415b]
+- Updated dependencies [6c8c956]
+- Updated dependencies [d5aba4b]
+- Updated dependencies [3ae0ca0]
+- Updated dependencies [1a1e2f2]
+- Updated dependencies [6c8c956]
+- Updated dependencies [1a1e2f2]
+- Updated dependencies [b6a90f9]
+- Updated dependencies [3424f9a]
+- Updated dependencies [6c8c956]
+- Updated dependencies [f4d3c87]
+- Updated dependencies [d601119]
+- Updated dependencies [ac5159a]
+- Updated dependencies [de1c8b5]
+- Updated dependencies [01e3a57]
+- Updated dependencies [6c8c956]
+ - solid-js@2.0.0-rc.7
+
## 2.0.0-rc.6
### Patch Changes
diff --git a/packages/web/package.json b/packages/web/package.json
index 8adbe1299..362bb29d6 100644
--- a/packages/web/package.json
+++ b/packages/web/package.json
@@ -1,7 +1,7 @@
{
"name": "@solidjs/web",
"description": "Solid's web runtime: client rendering, hydration, SSR, and DOM-specific control flow (Portal, Dynamic).",
- "version": "2.0.0-rc.6",
+ "version": "2.0.0-rc.7",
"author": "Ryan Carniato",
"license": "MIT",
"homepage": "https://solidjs.com",
@@ -398,7 +398,7 @@
"seroval-plugins": "~1.5.4"
},
"peerDependencies": {
- "solid-js": "^2.0.0-rc.6"
+ "solid-js": "^2.0.0-rc.7"
},
"devDependencies": {
"@codspeed/vitest-plugin": "^5.4.0",