Repository navigation
fix(signals): a memo waiting on a held flight is the hold's work at its landing (#4408, #4399) - #4510
Conversation
…ts landing (#4408, #4399) #4408 (F-6 amended 2026-10-28, D-26): `latest(m2)` showed `c1101` while `m1:1122` of the same lane was in flight. `m1` (an async memo over a verdict memo, no window of its own) was waiting on `m0`'s flight as the hold's work (verdict.ts `holdWork`), but kept CONFIG_VERDICT, so `m0`'s verdict-lane re-derive at the park re-derived it into the s lane early; the lane was unshown when `m1:22` landed and nothing held the frame `m0b:1100` landed into. `holdWork` now drops the verdict posture at the wait: the memo is the hold's work until a pass of it reads a verdict again (D-25: re-run by the write its hold holds, it reads plainly). The recompute head could not tell: the landing settles the node before the pass, and a lane's node is not held. #4399 (F-9, D-25): two actions, each holding a verdict memo's write, asked `m1:15` twice. `m1`'s pass over `m0b`'s landing re-entered its hold at its head, then read `m0` (A's verdict lane) first and was moved into that lane, where its read of its own hold's landing was the screen (D-26), so it asked `m1:15` again. In `enterLane`, a pass that re-entered the hold holding its node at its head stays that hold's work when the verdict lane is another hold's, as it already did when it read its hold's staging first (D-25: either read order). Its own hold's verdict lane is its display-ahead read and it still enters that one (#4050). Both decisions sit at the event (the wait's start in `propagateStatus`, a verdict-lane read in `enterLane`), behind `holdWorkHook` and the lane engine: nothing in recompute's hot path, 0 B in bundles without them. The pins in latest-two-readers-one-ask-4393 (beside) and latest-first-one-pass-4402 (#4399 split/value) flip from it.fails to it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
…4399) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
🦋 Changeset detectedLatest commit: 3565aa5 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)
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 |
|
The
This is the fix's own code: the guard in Per the batch process, this PR doesn't edit Generated by Claude Code |
Coverage Report for CI Build 41169306542Coverage remained the same at 78.108%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Merging this PR will improve performance by 5.12%2 warnings
Performance Changes
Tip Curious why performance improved? Comment Comparing Footnotes
|
…ct read (#4408 after #4504; D-25) After #4504, `m2 = m0b() + 1` (m0b a verdict memo) re-ran at the t landing while m0b was pending, threw, and got no posture (#4504: a pending read hands on none). Its next run, at m0b:1100's landing into the frame that m1:22's landing opened, was plain: it staged 1101 there, and `latest(m2)` showed it through that frame's verdict lane while m1:1122 was in flight (c1101 before v1122). #4504's rule rests on D-25's "the hold's own work ... reads plainly". It now applies only to such a pass: one born in the hold's world (uninitialized, D-27) or one over a staged pass of its own. A memo with a committed value and nothing staged is no hold's work. Its posture is what it reads, and it lasts "until one completes having asked nothing" (D-25, ruled 2026-10-28); a pass that threw on a pending read did not complete. The line stays behind observeFlightHook. holdWork returns from the wait branch instead of keeping a local (-10 B minified). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
|
I've re-fixed this after #4504 merged. That merge broke the The posture that went missing was The fix limits #4504's no-stamp rule to passes that are the hold's own work, per D-25 "born in its world … or re-run by the write it holds". Those are:
A memo with a committed value and nothing staged keeps its posture. D-25's "a memo's posture is what it reads" applies to it (#4050), and so does the 2026-10-28 ruling that a posture lasts until a pass completes having asked nothing. A pass that threw on a pending read did not complete. Each narrower form fails pins: uninitialized-only fails 5 of #4504's pins, and staged-only fails the 3 mid-flight pins. I also took one size cut from review. Tests after merging next:
The one web server failure ( Callgrind over Size vs next @ 71a6a75 for the whole PR, brotli / minified:
Hello world is unchanged. These rows are over their caps. The maintainer's size call is pending, and the caps move in the batch ledger. Generated by Claude Code |
|
The I don't think this PR causes it:
I've re-run the failed job once. The size gate is still red on its own growth, which is waiting on the maintainer's size call (see above). Generated by Claude Code |
|
CodSpeed flags two regressions on 5429aa0. I read both as noise:
This PR's change to Generated by Claude Code |
…nners (#4516) * test(signals): give the tree-shaking suite a 30 s timeout Every test in treeshake.test.ts runs a full Vite build of a fixture. The first one pays the bundler's cold start, and under the whole suite's load on CI it has run past vitest's 5 s default (5,295 ms on #4510, and on other PRs earlier today) without any assertion failing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2 * test(signals): widen the attribution self-time gap to 40 ms "measures self-time and aggregates costs by scope and root write" ranks a 10 ms memo above a cheap effect by wall-clock self-time. On this PR's CI run a scheduling pause inside the effect measured 17.9 ms against the memo's 10.1 ms. A 40 ms spin keeps the ranking meaningful under load. 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>
Re-records the rows #4511 left over cap or more than 20 B minified past the ledger on next @ 6bde511, and records #4510's growth (accepted by the maintainer, 2026-10-31) so it merges green once next is merged into it. Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2 Co-authored-by: Claude <noreply@anthropic.com>
Summary
Before:
latest(m2)showedc1101whilem1:1122of the same lane was still in flight.m1:15twice.After: both pins in
latest-two-readers-one-ask-4393(the "beside" row) andlatest-first-one-pass-4402(#4399's split/value rows) pass as plainit, with their expectations unchanged. The #4408 trace now readsv1122, thenc1101, thenv1123, which is the shape #4393 had before #4397 merged.How
Each decision is made at the event that makes the pass wrong, not by a per-pass check. Neither touches recompute's hot path, and both sit behind
holdWorkHookor the lane engine, so bundles without them pay 0 B.#4408 (
verdict.ts,holdWork):m1is an async memo over a verdict memo, with noisPending/latestwindow of its own. Whenm0goes pending,holdWorkmakesm1wait onm0's flight as the hold's work, butm1keptCONFIG_VERDICT. At the park,m0's verdict-lane re-derive (#4393) then re-derivedm1as a verdict reader into the s lane early. The lane was unshown whenm1:22landed, so nothing held the framem0b:1100landed into.holdWorknow clearsCONFIG_VERDICTwhen it says "wait" (D-25: the hold's work reads plainly; the pass's own reads stamp the posture again). recompute's head can't make this call: the landing settles the node before the pass runs, and a lane's node is never held.#4399 (
lanes.ts,enterLane):m1is held by the framem0b:20landed into. Its pass re-entered that hold at its head, then readm0(A's verdict lane) first and was moved into A's lane. There its read of its own hold's landing was the screen value (D-26), so it askedm1:15again. In the other read order (m0bfirst),creatorReadsalready keeps the pass as its hold's work.The guard gains one more case, in one mask test: a pass that is held, has no verdict posture, is not a frame, lagging or lane node, and whose hold is not
l's parent stays its hold's work (D-25: either read order). Its own hold's verdict lane is its display-ahead read, and it still enters that one (#4050), which keeps the #4393 rows green.Size (client, vs next d8ca095, brotli / minified, measured locally; no ledger edits in this PR):
signals: + isPending/latestapp: hydrating + every store primitive familypage: live server componentspage: compiled live server componentspage: live + routerHello world and every floor are unchanged (9,971 B). Bundles without the verdict engine are +0. The size check is expected to fail on
isPending/latest, compiled live and live + router; the batch ledger PR re-records them.Perf (callgrind on the prod dist, instructions per iteration): every changed bench moves by +0.005% or less (
propagation:diamond+149,propagation:avoidable+118,create1to1+447). None of those benches runs the changed code.How did you test this change?
The issues' pins in
tests/latest-two-readers-one-ask-4393.test.tsandtests/latest-first-one-pass-4402.test.tsflip fromit.failstoit, with their expectations unchanged.The async-gen differential was recorded on next with seeds 3289 and 91501 (2,000 cases each, all eight equivalence pairs) and replayed on the fix. 0 cases differ, and the finding signatures match next.
After merging next @ d8ca095:
Closes #4408
Closes #4399
Generated by Claude Code