Conversation
… the tick, net-equal writes propose nothing (#3494) A mainline write to a node a transaction holds — the same value again or another — is a second proposal for the same slot: the writer's tick joins the hold at the next flush's start (`batchJoins`, drained inside the running flush so nothing between the write and the flush is born the transaction's — the #3473 leak) and reveals with it. A tick whose writes net to the committed value proposed nothing: the adoption loop unstages an unstamped signal staged at its committed value, and computePendingState reads the same equality as final. Fixes the four #3473 regressions from #3494: the torn `[1,0,1]` effect input, the lost hide after a coalesced toggle, the stale visible derivation (a stale reader served a held memo's committed value counts as observing the memo's flight — reporterBlocksSource follows a dep's `_pendingSources` one hop), and the delayed release after a mainline `latest()` (a companion never blocks the settle — transitionBlocked skips `_parentSource` nodes). From differential fuzzing of the ruling (4 cohorts x 2 seeds x 5,000; 124 fixed / 7 new, the new ones one O1 family that follows from the rule plus a pre-existing P1): a pending mark rides only the links a pass made (A30 amendment — notifyStatus skips STATUS_PENDING over links outside the pass's validated prefix, `link._gen !== sub._depGen`, O(1)); and the no-proposal drop applies to unstamped nodes only (a held proposal rewritten to the committed value stays its transaction's). Spec: A34 new; A15/A28/A30 amended. Size: +175 B minified core floor, +36..+93 B brotli across scenarios; caps ratcheted with notes. Co-authored-by: Claude via Cursor <noreply@cursor.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Coverage Report for CI Build 35204286025Coverage remained the same at 71.46%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Merging this PR will not alter performance
Comparing Footnotes
|
|
Fixes #3494.
Ruling (A34 — new)
v: writingvagain is nothing — stages nothing, stamps nothing, pends nothing.vagain orw— is a second proposal on a contested node. It joins T, and so does the rest of that tick's batch.This reverses one mechanism choice from #3473, which removed
setSignal's eager entry from mainline to stop theactiveTransitionleak (a memo created after the write was born the transaction's, A29) — and dropped the grouping with it. The entry is now deferred:setSignalpushes the held node's transaction tobatchJoins;flush()enters each at its start, inside the running flush, adopting the tick's ambient batch. Recorded before the equality gate, so a repeat of the held value proposes too.The four #3473 regressions (all pinned in
tests/write-proposals-3494.test.ts)[1,0,1]effect inputbatchJoins)setShow(false); setShow(true)showstamped intocount's hold with nothing to reveal; nextsetShow(false)routed to the stamp and heldcomputePendingStateequality gateCount: 1besideCopy: 0)reporterBlocksSourcefollows a dep's_pendingSourcesone hoplatest(details)latest()shadow, backfilled under the transaction, blocked the settletransitionBlockedskips companionsFrom differential fuzzing (gabbev's fuzzer, #3446)
4 cohorts × 2 seeds × 5,000 cases, base =
origin/next. 124 fixed / 7 new.branches30→5 and 26→3;optimistic0/0 both sides.Two regressions the first cut introduced, both fixed and pinned:
_transition === nullonly.thisinside jsx-template inside a constructor of a derived class causes transformation-bug #2141): a hide joined to a parked action stopped reading a memo, unchanged, so A30 kept its dep tail; the action's truth re-asked the memo and the pending mark rode the kept link, registering the hidden reader as a reporter on a fetch nobody displays. A30 amended: a pending mark rides only the links a pass made —notifyStatusskipsSTATUS_PENDINGover links outside the pass's validated prefix (link._gen !== sub._depGen, the stamplink()already maintains; O(1)). Clears and errors still ride every link. (GatingreporterBlocksSourceinstead was wrong — it runs mid-pass — and the fuzzer caught it as an S1 flood.)The 7 remaining "new":
show/mountwritten in the same tick as an action's proposal is held by the action; toggling it back from mainline is a write to a held node → joins the action → the hide waits for the action to end. Base (post-feat(signals): A28 — a write becomes visible at flush, to every channel (read-side) #3473) publishes the hide immediately; pre-feat(signals): A28 — a write becomes visible at flush, to every channel (read-side) #3473 behaved like this PR. If a mainline write that nets a held node back to its committed value should escape the hold, that's a carve-out to A34(1) needing a ruling._transition. Follow-up.Size
Core floor +175 B minified (25,249 → 25,424; per-piece:
batchJoins71, drop 64,_genskip 24, one-hop 16). Brotli vs a freshnextbuild: +56 core, +75 store, +70 isPending/latest, +45 simple app, +80 hydrating, +36 hydrating+stores, +65 CSR, +93 observe, +40 observe+attribution, 0 frames. Caps ratcheted with notes. A golf pass replaced an O(kept-tail) link walk with the existing generation stamp and dropped a redundant companion snap; the fuzzer differential re-run on the golfed tree is case-for-case identical.Docs
A34 new; A15, A28, A30 amended; INTERNALS updated (
batchJoins, no-proposal drop, one-hop liveness, companion skip, pending-mark prefix rule); rules index regenerated; changeset.Claude via Cursor