Skip to content

fix(signals): writes that net in an action's tick pend nothing for a verdict reader (#4485) - #4498

Merged
ryansolid merged 7 commits into
nextfrom
claude/project-thread-x1ore2-4485
Oct 30, 2026
Merged

ryansolid merged 7 commits into
nextfrom
claude/project-thread-x1ore2-4485

Conversation

@ryansolid

Copy link
Copy Markdown
Member

Before: when an action opens a hold and, in the same tick, a signal is written and written back to its committed value (setS(2); setS(1)), a render effect reading [s(), isPending(s)] showed [1, true] for the whole hold, then re-ran at the landing as [1, false].

After: the effect shows [1, false] at once and does not re-run at the landing. Writes that net made no proposal (D-8), so the reader is not the hold's stale reader.

How: read's plain arm now checks netting before the verdict-reader stagedScreen arm. provisionalVerdict treats a netted signal staging as final: the reader is still the tick's verdict reader and is shown now (D-25), but it is not re-run at the landing. The test-only seat table gets the same netted check on its plain staged rows. #4367's opening tick is the same netted tick, so its interim pending frame is re-pinned to the D-8 screen (F). Its settle after the later writes is unchanged. RULES-DERIVED D-23's Status line records the fix.

Rules: D-8 (netted writes made no proposal), D-23 (stale reader of the tick's hold), D-25 (posture is the pass's), F-9.

Tests: tests/netted-verdict-reader-4485.test.ts (was it.fails, now passing); #4367 re-pinned in signals and web. Signals 6082 passed / 15 expected fail; web 1579 passed / 7 expected fail.

Size (client, vs next 643a7a7; no ledger edits in this PR):

Scenario Brotli Minified
signals: + isPending/latest 11,271 → 11,293 (+22) +69
page: live server components 38,969 → 39,073 (+104) +69
page: compiled live server components 42,931 → 42,959 (+28) +69
page: live + router 48,569 → 48,578 (+9) +75
app: hydrating + every store primitive family 32,470 → 32,437 (−33) +26

Every other scenario is unchanged. Hello world stays at 9,973 B. The measurement is deterministic across two builds.

Closes #4485


Generated by Claude Code

…verdict reader (#4485)

Writes that net to a signal's committed value, made in the tick an action
opens a hold (`setS(2); setS(1)` in the body after `setX(1)`, or on the
mainline just before it), made no proposal (D-8): the seam drops the
staging and nothing is held. A render effect reading `[s(), isPending(s)]`
still showed `[1, true]` for the whole hold and re-ran at the landing as
`[1, false]`. Its plain read took `read`'s verdict-reader `stagedScreen`
arm before the netted check, listing it as a stale reader of the flush's
hold, and its probe was the seam's verdict (`provisionalVerdict`): not
final, routed into the verdict lane with a re-run at the landing.

`read`'s plain arm now checks netting before the `stagedScreen` arm, and
`provisionalVerdict` answers a netted signal staging final: the reader is
still the tick's verdict reader, shown now (D-25), with no re-run at the
landing. The seat table's plain staged rows gain the same netted check
(SLOT_P, nothing raised), which also clears the world mismatches #4452's
netted read left in the shadow (10 classes -> 0).

#4367's opening tick (`act(3)` then a mainline `set(2)` on s = 2) is the
same netted tick, so its interim `pending` frame is re-pinned to the D-8
screen (`F`); its settle after the later writes is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
+69 B minified over next on every bundle that imports the verdict engine
(`provisionalVerdict`'s netted arm, and `read`'s plain arm no longer folding
into one ternary). Only "signals: + isPending/latest" crosses its cap:
11.12 -> 11.14 KB at measured 11,130 B + 10 B, recorded minified 30,641 ->
30,725 B. The live server-components frozen floor stays within its cap
(38,816 B against 38.85 KB) and is not raised.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
seat.ts: next's #4452 netted rows (D1''/L1''/V3'') and this branch's
early netted return meet; the early return keeps next's row ids.
D-23 Status: the #4485 pin note becomes the fixed note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
@changeset-bot

changeset-bot Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 31d7847

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 15 packages
Name Type
@solidjs/signals Patch
test-integration Patch
@solidjs/web Patch
@solidjs/babel-plugin Patch
@solidjs/compiler Patch
@solidjs/diagnostics Patch
@solidjs/element Patch
@solidjs/h Patch
@solidjs/html Patch
solid-js Patch
@solidjs/universal Patch
board-example Patch
canvas-example Patch
server-dbmon-example Patch
todos-server-example Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Size (brotli, eager entry chunk)

scenario head vs base minified vs base minified vs recorded cap lazy chunks (not counted)
signals: core floor (createSignal/Memo/Effect/Root/flush) 7.42 KB +11 B (+0.1%) 0 B +15 B 7.43 KB ✅
signals: + createStore 14.98 KB +22 B (+0.1%) 0 B +32 B 14.97 KB ❌ over by 9 B
signals: + isPending/latest 11.34 KB +45 B (+0.4%) +110 B +144 B 11.29 KB ❌ over by 48 B
signals: + until/refresh (truth readers, no optimism) 8.04 KB −1 B (−0.0%) 0 B +2 B 8.06 KB ✅
app: render + one signal (the simple-app floor) 9.78 KB −7 B (−0.1%) 0 B +15 B 9.78 KB ⚠️ over by 1 B, 5 B minified headroom
app: hydrating (no stores) with Show/For/Loading/Errored/lazy 18.73 KB +10 B (+0.1%) 0 B 0 B 18.74 KB ✅ lazy-page.js 0.04 KB
app: hydrating + every store primitive family 32.48 KB +10 B (+0.0%) 0 B +26 B 32.48 KB ✅ lazy-page.js 0.04 KB
app: CSR with Show/For/Loading/Errored/lazy 13.45 KB 0 B 0 B +9 B 13.47 KB ✅ lazy-page.js 0.04 KB
app: CSR, observe tier (same app on the observe artifacts) 15.19 KB −3 B (−0.0%) 0 B +9 B 15.20 KB ✅ lazy-page.js 0.04 KB
app: CSR, observe tier + attribution engine enabled 29.78 KB +16 B (+0.1%) 0 B +9 B 29.78 KB ✅ lazy-page.js 0.04 KB
app: compiled floor (one template, one text hole, one delegated click) 9.97 KB −2 B (−0.0%) 0 B +2 B 9.98 KB ✅
app: compiled CSR (JSX todo app: spread/merge/omit, events, class/style, keyed For, Show, Loading + lazy, store) 26.35 KB +40 B (+0.2%) 0 B +39 B 26.33 KB ❌ over by 16 B stats.js 0.18 KB
app: compiled hydrating (the same JSX todo app through hydrate(), compiled hydratable) 32.48 KB +9 B (+0.0%) 0 B +33 B 32.54 KB ✅ stats.js 0.20 KB
frames: eager client consumer (frames client + transport, lazy codec) 11.75 KB 0 B 0 B 0 B 11.77 KB ✅
page: base server components (hydrating + dynamic + frames + sf reference) 33.72 KB −14 B (−0.0%) 0 B +31 B 33.77 KB ✅ assets.js 0.78 KB, decode.js 7.09 KB, lazy-page.js 0.04 KB, regions.js 1.06 KB, trace.js 9.17 KB, wire.js 3.47 KB
page: live server components (base + live/GET + action + isPending/latest) 39.09 KB +65 B (+0.2%) +110 B +175 B 38.98 KB ❌ over by 109 B assets.js 0.78 KB, decode.js 7.09 KB, lazy-page.js 0.04 KB, regions.js 1.06 KB, trace.js 9.19 KB, wire.js 3.47 KB
page: compiled base server components (the base page as JSX: templates with class/style/attributes/events, For/Show; no spread) 35.71 KB −38 B (−0.1%) 0 B +33 B 35.72 KB ✅ assets.js 0.78 KB, decode.js 7.09 KB, regions.js 1.06 KB, sc-comments.js 0.20 KB, trace.js 9.14 KB, wire.js 3.47 KB
page: compiled live server components (the compiled base page + live/GET + action + isPending/latest) 43.00 KB +41 B (+0.1%) +108 B +163 B 42.95 KB ❌ over by 46 B eager (counted): web.js 22.98 KB; assets.js 0.78 KB, decode.js 7.09 KB, regions.js 1.07 KB, sc-comments.js 0.19 KB, trace.js 9.18 KB, wire.js 3.47 KB
page: base + router (base page + @solidjs/router: createRouter, two routes, preload, useNavigate) 41.11 KB −86 B (−0.2%) 0 B +36 B 41.25 KB ✅ assets.js 0.78 KB, decode.js 7.09 KB, lazy-page.js 0.04 KB, regions.js 1.07 KB, server.js 1.03 KB, serverForms.js 3.58 KB, trace.js 9.11 KB, wire.js 3.48 KB
page: live + router (live page + @solidjs/router: createRouter, two routes, preload, useNavigate) 48.64 KB +95 B (+0.2%) +107 B +170 B 48.58 KB ❌ over by 60 B eager (counted): client.js 28.13 KB; assets.js 0.78 KB, decode.js 7.09 KB, lazy-page.js 0.04 KB, regions.js 1.07 KB, server.js 1.03 KB, serverForms.js 3.30 KB, trace.js 9.13 KB, wire.js 3.49 KB
server: floor (getRequestEvent + isServer) 1.27 KB 0 B 0 B 0 B 1.29 KB ✅
server: renderToString (the server-render floor) 22.79 KB 0 B 0 B 0 B 22.80 KB ✅

⚠️ Over the brotli cap within the minified allowance (passes)

  • app: render + one signal (the simple-app floor): over brotli cap by 1 B; minified 26,754 B vs 26,739 B recorded with the cap (+15 B) — 5 B of the 20 B minified allowance left; +0 B minified over this PR's base

❌ Fails the gate

  • signals: + createStore: over brotli cap by 9 B; minified 43,956 B vs 43,924 B recorded with the cap (+32 B) — 12 B past the 20 B minified allowance; +0 B minified over this PR's base. real growth: reduce it, or raise the cap and its recorded minified in this PR with a reason (frozen floors need Size-Exception:)
  • signals: + isPending/latest: over brotli cap by 48 B; minified 31,245 B vs 31,101 B recorded with the cap (+144 B) — 124 B past the 20 B minified allowance; +110 B minified over this PR's base. real growth: reduce it, or raise the cap and its recorded minified in this PR with a reason (frozen floors need Size-Exception:)
  • app: compiled CSR (JSX todo app: spread/merge/omit, events, class/style, keyed For, Show, Loading + lazy, store): over brotli cap by 16 B; minified 80,074 B vs 80,035 B recorded with the cap (+39 B) — 19 B past the 20 B minified allowance; +0 B minified over this PR's base. real growth: reduce it, or raise the cap and its recorded minified in this PR with a reason (frozen floors need Size-Exception:)
  • page: live server components (base + live/GET + action + isPending/latest): over brotli cap by 109 B; minified 118,033 B vs 117,858 B recorded with the cap (+175 B) — 155 B past the 20 B minified allowance; +110 B minified over this PR's base. real growth: reduce it, or raise the cap and its recorded minified in this PR with a reason (frozen floors need Size-Exception:)
  • page: compiled live server components (the compiled base page + live/GET + action + isPending/latest): over brotli cap by 46 B; minified 126,181 B vs 126,018 B recorded with the cap (+163 B) — 143 B past the 20 B minified allowance; +108 B minified over this PR's base. real growth: reduce it, or raise the cap and its recorded minified in this PR with a reason (frozen floors need Size-Exception:)
  • page: live + router (live page + @solidjs/router: createRouter, two routes, preload, useNavigate): over brotli cap by 60 B; minified 143,439 B vs 143,269 B recorded with the cap (+170 B) — 150 B past the 20 B minified allowance; +107 B minified over this PR's base. real growth: reduce it, or raise the cap and its recorded minified in this PR with a reason (frozen floors need Size-Exception:)

Bundled with Rolldown (what Vite ships), brotli q11, decimal KB. A scenario fails only when it is over its brotli cap and its minified size is more than 20 B over the minified recorded with the cap; over the cap within that allowance is brotli layout noise and passes with a warning. Caps and their recorded minified in scripts/size/scenarios.js; the floor and page caps in floor-caps.json are frozen (lower only, or Size-Exception: in the PR body). npm run ratchet lowers caps per RC; it never raises one (scripts/size/README.md).

@coveralls

coveralls commented Oct 30, 2026 •

Copy link
Copy Markdown

Coverage Report for CI Build 41126172154

Warning

Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes.
Quick fix: rebase this PR. Learn more →

Coverage remained the same at 78.1%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 1362
Covered Lines: 1125
Line Coverage: 82.6%
Relevant Branches: 1090
Covered Branches: 790
Branch Coverage: 72.48%
Branches in Coverage %: Yes
Coverage Strength: 29.65 hits per line

💛 - Coveralls

ryansolid commented Oct 30, 2026 •

Copy link
Copy Markdown
Member Author

Size gate (check), updated after #4501 re-recorded the ledger and this branch merged it (0001839): the rows still over are this PR's own measured growth, as listed in the body. isPending/latest is +69 B minified, live server components +69 B (+104 B brotli), and compiled live server components +69 B. Under the current batch process, fix PRs carry no ledger edits, and these rows are re-recorded in one ledger PR after the batch merges. Tests (job) pass.

CodSpeed on 0001839 flags one bench, projection derive: write one NESTED field (reference) (−11.4%). That is a reference bench that swings ±13–15% with module byte size alone; CodSpeed itself warns it ran on different hardware, and the comparison base is bb7f9b5 rather than current next (14a8190) because no next run had finished. The diff doesn't touch the projection or store write path that bench exercises, and the other 199 benches are unchanged, so I'm not treating it as this PR's regression.


Generated by Claude Code

@codspeed

codspeed Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Merging this PR will improve performance by 14.22%

1 warning

⚠️ 4 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

⚡ 1 improved benchmark
✅ 199 untouched benchmarks
⏩ 5 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
⚡ projection derive: write one NESTED field (reference) 2.4 ms 2.1 ms +14.22%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing claude/project-thread-x1ore2-4485 (31d7847) with next (6af149e)

Open in CodSpeed

Footnotes

  1. 5 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

…lue's call site

Restores read's single return-the-screen tail (the netted staging returns
the committed value through it, shadow slot P) and moves the D-8 netted
route out of provisionalVerdict into its one caller in verdictValue, so
provisionalVerdict keeps its old signature. No behaviour change: signals
6103 passed / 15 expected fail; web 1581 passed / 7 expected fail.

Pins #4367's re-pinned frames exactly (run counts included) instead of
de-duplicated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
@ryansolid
ryansolid merged commit b5077ea into next Oct 30, 2026
7 of 8 checks passed
ryansolid added a commit that referenced this pull request Oct 30, 2026
…4499, #4500, #4493, #4488, #4498) (#4507)

next @ b5077ea is over six caps after the batch merged back to back (each
fix PR carried no ledger edits, per the batch process). Re-records,
measured over next @ b5077ea (linux):

- signals: + createStore: 14.97 -> 14.99 KB, minified 43,924 -> 43,956
- signals: + isPending/latest: 11.29 -> 11.35 KB, minified 31,101 -> 31,245
- app: compiled CSR: 26.33 -> 26.36 KB, minified 80,035 -> 80,074
- page: live server components (frozen floor): 38.98 -> 39.10 KB,
  minified 117,858 -> 118,033
- page: compiled live server components: 42.95 -> 43.01 KB,
  minified 126,018 -> 126,181
- page: live + router: 48.58 -> 48.65 KB, minified 143,269 -> 143,439

and the recorded minified only (caps unchanged) where the batch drifted
more than 20 B: hydrating + every store (98,535 -> 98,561), compiled
hydrating (100,803 -> 100,836), base server components (frozen,
101,675 -> 101,706), compiled base server components (108,048 ->
108,081), base + router (125,503 -> 125,539).

Hello world is 9,971 B (cap 9.98 KB). The build leaves the pinned
mangle-props.json unchanged.


Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants