Repository navigation
fix(signals): a merged transaction's verdict lane is the same hold's at the D-28 seat (#4427) - #4455
Conversation
…ane objects (#4427) `laneRead`'s D-28 reset (#4275, #4284) makes a leaf seated as a hold's verdict reader leave the seat when it reads a lane that is not that hold's. It decided that by lane identity (`passLane !== l`). A transaction merged into the hold keeps its own verdict lane: a derivation stamped before the merge stays in it. So the leaf read that lane as another hold's. It was served the derivation's staging as the hold's own work (L7'), and its run waited for the hold's landing while the derivation it read was already on screen. The reset now compares the holds. It leaves only when the node is in flight and has never shown, when the lane is not a verdict lane, or when the lane's parent resolves to a different hold than the seat's (D-4: merged holds are one). The seated leaf then reads that lane as the hold's own: the lane's value, the two lanes linked by `enterLane` as for any pass that reads both. Fixes #4427 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
…golf readsHeld's loop `readsHeld` (#4449) now walks its deps as `retryReaches` does (`for (let d = r._deps; d; d = d._nextDep)`), which saves 7 B minified on isPending/latest. Measured locally (linux) over next @ 7135a77. On page: live server components (a frozen floor): 38,764 B brotli / 117,272 B minified, +45 B minified, so the cap goes 38.76 -> 38.78 KB and floor-caps.json's recorded minified 117,227 -> 117,272. On page: compiled live server components: 42,690 / 125,414, so the cap goes 42.67 -> 42.70 KB and the recorded minified 125,374 -> 125,414. Caps are at measured + 10 B rounded up to 0.01 KB. Every other scenario passes the gate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
🦋 Changeset detectedLatest commit: 828aa89 The changes in this PR will be included in the next version bump. This PR includes changesets to release 15 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)
|
Coverage Report for CI Build 41065411805Coverage remained the same at 78.1%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Merging this PR will degrade performance by 13.54%2 warnings
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
The #4400 case writes s, waits 3 ms, and writes again while a 10 ms ask is in flight. On CI's coverage run the real gap outlived the ask, which landed before the second write and asked `m1:23` (seen on #4455 and #4460). The gaps are part of each scenario, so the file now advances fake timers instead of sleeping. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
…er it lands (#4460) * fix(signals): don't retain the transaction a latest() read opened after it lands `pendingVerdict` (#4453) remembered the flush's transaction a `latest` read of an unheld flight opened in a module-level `latestJoined`, compared only against `flushTransaction` and never cleared: after the seam it kept the last landed transaction (its `_nodes`, `_queues`, `_reruns`) alive until the next such read overwrote it. The mark now lives on the transaction itself (`Transaction.latest`), so it ends with it. Same answer for every probe: a transaction stays the flush's from its opening to the seam, a merge keeps the flush's own, and one merged away is no longer anyone's flush transaction, as before. The property is not `_`-prefixed (as `folded`), so the mangler's assignment is untouched; only bundles importing `latest`/`isPending` change, and they shrink (isPending/latest -13 B brotli, -10 B minified). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2 * test(signals): the #4393/#4400 two-reader specs run on fake timers The #4400 case writes s, waits 3 ms, and writes again while a 10 ms ask is in flight. On CI's coverage run the real gap outlived the ask, which landed before the second write and asked `m1:23` (seen on #4455 and #4460). The gaps are part of each scenario, so the file now advances fake timers instead of sleeping. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2 --------- Co-authored-by: Claude <noreply@anthropic.com>
…-x1ore2-4427 # Conflicts: # scripts/size/floor-caps.json # scripts/size/scenarios.js
…-x1ore2-4427 # Conflicts: # scripts/size/scenarios.js
|
I don't think CodSpeed's −13.5% on Generated by Claude Code |
… held-frame clause covers it laneRead's D-28 seat reset goes back to comparing verdict lanes by identity. With the held-frame clause, no shape depends on the hold comparison, and the seat table's #4427 known entry no longer matches anything. -48 B minified on the verdict pages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
Summary
Before: a render effect seated as a hold's verdict reader (it reads
latest()of a node B holds) left that seat when it read a verdict-lane derivation of a transaction merged into B. That derivation was stamped before the merge and still sits inA._verdict.D-28's reset compared lane objects, so B's own merged lane looked like another hold's. The leaf was then served the staging as B's held work, and its run waited for B's landing, while the derivation it read was already on screen. The screen showed
m = 20beside a leaf still at1/10, a tear (F-1).After: the reset compares holds, so a merged transaction's verdict lane counts as the same hold's (D-28, D-4). The leaf stays seated, reads the lane's value, and shows it together with the derivation (
1/20).How
In
laneRead, a seated leaf now leaves the seat only in three cases:Otherwise the leaf reads the lane as its own: it skips the stale-reader block, and
enterLanelinks the two verdict lanes of the one hold. This is the V6 row of #4438's table.This only runs with the lane engine installed. Nothing changes in
recompute, and no effects are added. The structural fix for lanes keeping their pre-merge identities (#3905) is out of scope.Also:
readsHeld's loop (from #4449) is rewritten asfor (let d = r._deps; d; d = d._nextDep), asretryReachesdoes, per the #4449 review. That saves 7 B minified. The #4438 seat-shadowknown #4427entry now gets 0 hits and is left in place for later cleanup.Size vs next @ cddaa76 (measured locally, linux; next's ledger was re-recorded by #4462):
signals: + isPending/latestapp: hydrating + every store primitive familypage: live server components(frozen floor)page: compiled live server componentspage: live + routerThe brotli on the store scenario is mostly layout: the
lanes.tschange renames a mangled property instore.js, which moves brotli on bundles whose minified size barely changes.Size-Exception: the live server-components page carries the D-28 same-hold check that fixes #4427 (+45 B minified, +22 B brotli); correctness.
How did you test this change?
packages/signals/tests/verdict-seat-merged-lane-4427.test.ts. Onnextthe leaf stays at1/10besidem = 20; with the fix it passes.server-functions-adapter-requestfails the same way onnext.next, the new test hits onlyknown #4427; with the fix it hits nothing.Fixes #4427
Generated by Claude Code