Repository navigation
fix(signals): a Show branch mounted by a held write reads the value that mounted it (#4010) - #4027
Conversation
…hat mounted it (#4010) A binding in a branch a held pass mounted is a member of that pass's held frame (ruling A): the branch exists only in the transaction. Only a member that had never run (STATUS_UNINITIALIZED) was treated as born into it; one whose first pass published before the seam held it was served the committed screen whenever it ran again before the landing — `frameRead` made it a stale reader, `recompute` published it mainline, and `stagedScreen` served a verdict reader the committed value in the transaction's own flush. A non-keyed Show's narrowed accessor then read the committed `null` and threw into <Errored>; since #3977 an unchanged landing left the error up. The park seam now flags every member of a held pass's frame (CONFIG_HELD_FRAME, cleared by its commit), and `bornInto(c, t)` — held by t and uninitialized or flagged — replaces the uninitialized-only test in `recompute`, `frameRead` and `stagedScreen`. `observeFlight` likewise no longer serves such a verdict reader the committed value of its own transaction's flight: it waits with its frame. Without that, a member that probes and reads the flight let the transaction land under it and was re-derived into a fresh observation every round (an infinite flush loop). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NcUAu8BQ6Xsf8XRBhr9V1w
🦋 Changeset detectedLatest commit: 73c288c The changes in this PR will be included in the next version bump. This PR includes changesets to release 13 packages
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 |
Size (brotli, eager entry chunk)
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 |
Generated by Claude Code |
Coverage Report for CI Build 38436719295Coverage remained the same at 77.429%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Merging this PR will improve performance by 9.56%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ⚡ | createStore setter: delete + set one root key (#3044 overlay) |
2.1 ms | 1.8 ms | +13.5% |
| ⚡ | memo + sync render effect only (reference) |
28.2 ms | 26.6 ms | +5.75% |
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-w8qjvi-4010 (73c288c) with next (25935fe)
…verdict-reader-4010
Accepted by the maintainer: +89 B minified in the always-loaded signals core. Each cap is set at measured + 10 B rounded up to 0.01 KB; hello world stays under 10 KB (9,966 B). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NcUAu8BQ6Xsf8XRBhr9V1w
Generated by Claude Code |
…verdict-reader-4010
…-w8qjvi-4007 Resolves the attribution scenario cap conflict with #4027: keeps next's #4010 note and re-measures this PR on top of it (28,984 -> 29,230 B, cap 29.24 KB), accepted by the maintainer 2026-10-13. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NcUAu8BQ6Xsf8XRBhr9V1w
Requested by Ryan · project thread
Fixes #4010.
Before: when a held write mounts a
<Show>branch, a binding inside it that re-runs before the transaction lands (anisPendingreader, or a plain binding re-run by a live write) is served the committed screen, where the Show condition is stillnull.item().namethrows into<Errored>, and since #3977 the unchanged landing never re-runs it, so the error sticks.After: the branch's bindings read the transaction that mounted them until it lands. No error, and the final screen is correct.
How:
holdFramealready visits exactly the held frame's members, so it now tags each with an internalCONFIG_HELD_FRAMEbit (cleared incommitPendingNodewithCONFIG_HELD, and defensively at the three lane sites that dropCONFIG_HELD). A new internalbornInto(c, t)treats a tagged member like a never-run node, born intot. It is used inframeRead,stagedScreenandrecompute.observeFlight(inverdict.ts, so onlyisPending/latestusers pay) makes such a member wait on its own transaction's flight; without that gate a keyed branch that probes and reads the flight loops forever. Each of the four sites is needed: removing any one fails at least one new test.Size
+89 B minified in the always-loaded signals core (+121 B with
isPending/latest). Ten scenarios crossed their caps; each is raised to measured + 10 B rounded up to 0.01 KB, with a dated ledger note inscripts/size/scenarios.js. Two of them are frozen floor caps infloor-caps.json: core floor 7.45 -> 7.48 KB and the simple-app floor 9.94 -> 9.98 KB. Hello world stays under 10 KB at 9,966 B.Size-Exception: accepted by the maintainer (2026-10-13) — #4010's held-frame tag lives in the always-loaded core read path; hello world stays under 10 KB.
Behavior note
A never-run verdict reader that reads its own transaction's held flight now waits on it instead of seeing the committed value. Consistent with A29 ("a node born into the future has no screen"); no existing test pinned the old behavior.
Public API changes
None.
bornIntoandCONFIG_HELD_FRAMEare internal.How did you test this change?
packages/signals/tests/held-branch-verdict-reader-4010.test.ts(4 tests) andpackages/web/test/show-held-branch-verdict-reader-4010.spec.tsx(the reporter's repro, count 0 and 1). All fail on unmodifiednext.next(server-functions-adapter-request.spec.tsx). Typecheck clean.scripts/size: every scenario within its cap;check-floor-capspasses with the Size-Exception line.🤖 Generated with Claude Code
https://claude.ai/code/session_01NcUAu8BQ6Xsf8XRBhr9V1w