Patch channel round-6 hardening + patch-mode default flip - #3091
Patch channel round-6 hardening + patch-mode default flip#3091ryansolid wants to merge 7 commits into
Conversation
Prod-sound getter demotion (accessed-key recording + bounded adoption probes), transition-merge same-channel coalescing, unbind-safe structural queue dispatch, fixed dispatch windows, initial-construction severing on throw, and active failed-apply resync. Co-authored-by: Cursor <cursoragent@cursor.com>
Both compilers now emit patchDriver/rowProof templates by default (opt out: patchDriver: false). Regenerated fixture outputs and Oxc expectations; parity tier dom-nopatch fences the opt-out. Size scenarios renamed from flip-preview to default-on and ratcheted with round-6 notes. Includes AUDIT-BRIEF-R6.md for the pre-merge audit. Co-authored-by: Cursor <cursoragent@cursor.com>
🦋 Changeset detectedLatest commit: 36d1d38 The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 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-limit report 📦
|
Coverage Report for CI Build 33239878602Warning Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes. Coverage remained the same at 71.652%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Merging this PR will improve performance by 44.18%
Performance Changes
Tip Curious why performance improved? Comment Comparing Footnotes
|
|
Post-round-6 + default-flip dbmon (octane harness, alternating 30-iter rounds, midday machine — medians in ms):
Tick at 1.8 matches the recording+probe expectation measured when A1 landed (1.8 vs 1.7 with no demotion check vs 1.9 full-scan) — the round-6 hardening added no regression beyond the already-priced probe cost. Partial-tick diff-skip ratio 0.28–0.30 (better than octane's 0.32). Semantic gate green both rounds. |
|
Patch-mode worth-proof: same branch, same runtime, same fixture source — the ONLY difference is the compiler flag (three-way alternating, 30 iters, medians ms):
Patch mode is what puts this next to octane: classic sits 4.2x behind on the full tick; patch is within ~19%. The ~1.5–3.6 kB bundle cost in the size scenarios buys this column. (Incidentally the flip surfaced in the harness itself: the fixture's opt-out env passed empty compiler options, which now mean patch-ON — fixed to explicit |
Contract-derived tests, not instance reproductions: accessor-safety across admission/adoption axes, one-apply-per-batch under lane interleaving, consumer-list lifecycle at emission/drain/merge boundaries, throw-atomicity at every row-build entry point, hydration region surrender, Tier-2 read-set completeness, and compiled-imports ⊆ documented-runtime-surface contracts. 10 red / 8 green against the round-6 branch; each red is a confirmed audit-7 finding, each green documents an invariant that already holds. Co-authored-by: Cursor <cursoragent@cursor.com>
Each fix flips a red invariant-harness test green: stateless adoption probes against the incoming backing, prototype-plainness admission, split normal/optimistic stamps, emission-snapshot structural queues + drain-live value lists (with cross-release coalescing), build-before- destroy slot rebuilds, and full server-region surrender on hydration throw. Store+core suites 465 green; driver suites green. Co-authored-by: Cursor <cursoragent@cursor.com>
…1; renderer surface documents patchDriver (P1-4) Both compilers emit the body's full static read envelope (hoisted _mf$ arrays, deduped per module); the runtime interns manifests by identity, probes deep paths as a prefix tree at adoption gates and forced applies, bubbles ancestors on targeted reconciles, and resolves forced next through the proxy for deep-path channels. Eligibility: bare-subject reads and dotted string keys compile classic. Renderer type/README/contract tests pin patchDriver into the documented surface. PINV per-flush ledger checks wired into the __TEST__ invariant infra. dbmon: tick 2.1ms quiet-machine (was 1.9 unsound / 2.5 first sound cut), mount intern misses eliminated. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Round 7 pushed — all nine audit findings fixed, harness-first. Three commits:
Also new: PINV-1..3 per-flush channel-ledger assertions in the Perf, quiet machine: dbmon tick 2.1 ms vs round-6's 1.9 — the +0.2 is the honest price of sound accessor demotion across branches and nested chains (first sound cut measured 2.5; manifest interning, prefix-tree probing, and leaf inlining recovered the rest — two profiler passes attached to the work, not guesses). Partial tick still beats octane (0.5 vs 0.7). Mount's intern misses were profiled to zero after array hoisting; final mount number needs one quiet-machine confirmation (a parallel build was loading the box during the last sweep). Gates: 32/32 monorepo tasks, size scenarios ratcheted with dated notes (net round-7 cost: ~+0.3 kB store apps, ~+0.6 kB patch-list apps), changesets included. |
|
Quiet-machine confirmation sweep (evening, 80% idle, both orders, 30 iters):
Mount fully recovered — the That closes round 7 end to end: 9/9 findings fixed harness-first, gates green, perf within noise of the pre-audit numbers with the soundness holes closed. |
Admission deep-probes manifests + committed-view reads, captured-record structural binds (patchProxyFor via createTarget-installed wrap hook — keeps the trap engine shakeable), lane-timed tentative ancestor bubbles with settle twins, generation-stamped drains, forced-bubble coalescing, decimal-key ineligibility (both compilers), rowProof on the createRenderer surface, untracked universal commit phase. dbmon unchanged (6.4/2.0/0.5). Co-authored-by: Cursor <cursoragent@cursor.com>
|
Round 8 pushed — 8/8 findings fixed, harness-first again (the new invariant tests committed red where reproducible). Highlights beyond the straight fixes:
Everything else as reported: manifest deep-probe at admission, decimal-key ineligibility in both compilers (byte-parity fixtures regenerated), Gates: 32/32 tasks, 4,323 compiler + 153 Babel + 468 signals + 71 driver tests, size ratcheted (+~0.3 kB per tier with dated notes). dbmon identical to round-7 finals: mount 6.4 / tick 2.0 / partial 0.5, quiet machine, both orders. |
Committed-visible skip markers + held-view admission, optimistic-view mounts, manifest-read fallback compute (write-free), tentative self emission + immediate lane demotion, per-queue forced stamps + merge repair, isWrappable binds, server patch-tier exports, function-carrier probes, safe-int keys, unchanged-reconcile bubble gate. Attribution test warn-count flake fixed two-sided. dbmon 6.3/2.1/0.6 (parity with r7/r8). Co-authored-by: Cursor <cursoragent@cursor.com>
|
Round 9 pushed — 11/11 findings addressed, harness-first, with three findings materially refined during verification:
The rest as reported: per-queue forced-stamp clearing + merge repair, isWrappable bind guards, server-entry Gates: 32/32 tasks, 4,325 compiler tests, size gates ratcheted with dated notes. dbmon 6.3 / 2.1 / 0.6 — parity with rounds 7–8. Standing note for the next design pass (also in the brief): rounds 7–9's P1s are dominated by hand-replicated visibility rules at channel seams. I'm prototyping node-driven delivery (one hidden node per patched record, compiled bodies unchanged) as the by-construction answer before the next audit round — if per-record nodes hold dbmon, that eliminates the timing-divergence class entirely. |
|
Review note from #3161 (For + siblings hydration desync, fixed in 91e300a): the regression came from the list seam's lazy |
Audit brief — round 6 + patch-mode default flip
Scope:
next..patch-hardening-r6. Two bodies of work: (A) fixes for thesix round-6 findings against
adf10e9b, (B) the patch-mode DEFAULT-ON flip(both compilers). Everything below states what changed, the soundness claim,
and — most useful to attack — the reasoning each claim depends on.
A. Round-6 findings
A1. Prod-sound getter demotion (was: dev-only — reverted)
The dev-only trade is gone. Design: accessed-key recording + bounded
probes.
patchDriver(web) runs the registration-time initial force-apply througha recording
Proxyand hands the read set toregisterPatch(record, fn, keys). Hydration registrations (no initial apply) record at their FIRSTdrain apply instead (
applyEntries,entry.k).pc.ak(deduped array). Both adoptionemission seams (reconcile walk, fold commit) gate on
targetKeysPlain:probe ONLY
ak's keys for own getters on the adopted backing;ak === null(registered-but-never-applied) falls back to the full scan.
are grammar-guaranteed sequences of
if (force || n.k !== p.k) { write reading n.k }— under force the COMPARES short-circuit but every WRITEexecutes and reads its keys; under non-force first applies the compares
read both sides. Is there any compiled body shape whose key read is
conditional on something other than
force/compare? (Eligibility grammar:pure member chains of one subject — check
wrapPatchModeemission shapes.)akis a UNION across registrations and nevershrinks; adoption probes are
O(|ak|)per patched-record adoption.Measured on dbmon: tick 1.8 ms vs 1.7 no-check vs 1.9 full-scan (midday
machine; re-measure welcome).
A2. Transition-merge collisions coalesce (
scheduler.ts)Same-channel entries in BOTH stashes now merge to ONE entry that resolves
nextLIVE at drain (entry.t = pc.t, drain readst.pb ?? t.v), keepingthe destination's
prev. Attack: theprevchoice — both captures arecommitted pre-write values of the same record; are there merge orders where
they differ and the kept one is wrong? Also the opaque backref contract
(core mutating
entry.pc.qa/qe/t) — is any other holder of these fieldssurprised?
A3. Row/slot queued work respects unbinds (
patch.ts)Emitters no longer clone wrapper entries; queue items carry the LIVE
registration list plus payload (
ops/si), dispatched byapplyStructuralwith the same unbound-mark (entry.u) + disposed-ownerchecks and error routing as value patches. Attack: ordering — value
entries and structural entries interleave in emission order; the live-list
change means late registrations see earlier-queued structural work. Driver
double-applies? (registerRowOps consumers are driver-internal only.)
A4. Dispatch windows (
applyEntries)Snapshot for multi-consumer lists; FIXED length window + undefined guard for
the single-consumer alias (a callback registering another patch mid-dispatch
must not run it in the same drain — it just received its initial apply).
Attack: entry removed mid-dispatch shifts the aliased single-entry list —
covered by the undefined guard?
A5. Initial list construction severs on throw (
patch-driver.ts)Client + hydration first-build loops now sever completed rows' registrations
AND the throwing row's partials, remove inserted/claimed DOM (including the
claimed server row under hydration), dispose the list owner, rethrow.
Attack:
patchCountaccounting across sever-then-rethrow; boundaryremount re-engagement.
A6. Failed-apply recovery is ACTIVE (
patch-driver.ts)resyncNeeded+ slot ticks now trigger an immediate identity resync (deepvalue-only recovery still waits for the next list event — documented).
Identity swaps register the new subject's channels BEFORE the apply.
Attack: resync loops when the poison row keeps throwing (flag stays set,
retried per event — bounded?).
B. Default flip (patch mode ON)
config.patchDriver: "patchDriver"; Rustpatch_driverresolvesWrapper::Defaultlike every other wrapper (opt out:false). The JSloader already normalizes
true/absent.dom-patchreplaced by
dom-nopatch(fences the explicit opt-out — plaindomnowcovers patch grammar). Byte parity previously held on the whole corpus
with patch on (108/108, zero ratchet files).
size-scenario notes, docs, the
driveList"compiler is default-on"comments (now true), octane fixture flags (now redundant), the loader's
patchDriver: truenormalization interacting with default-on.typical apps (value tier), ~+3.6 kB store-list apps, portal-swarm ~5%
effect-fallback tax on signal-only mount churn.
Standing accepted trades (pre-existing, documented)
retention churn (design §21a).
lost for demoted rows) — §20.
event (A6).
Test map
packages/signals/tests/store/patch-channel.test.ts— channel semantics,all rounds' regressions (31+ tests).
packages/web/test/for.patchlist.spec.tsx— driver incl. exceptionatomicity, severing, recovery (15+ tests).
packages/web/test/for.equivalence.spec.tsx— driver ≡ classic matrix.packages/compiler/__tests__/parity*— Babel↔Oxc byte parity (dom =patch-on, dom-nopatch = opt-out).