Skip to content

fix(signals): five rule-decided fixes from the semantic fuzzer (F3, F4, F9, F10/F11, F13) - #3801

Merged
ryansolid merged 8 commits into
nextfrom
fix/fuzz-batch-a
Oct 5, 2026
Merged

ryansolid merged 8 commits into
nextfrom
fix/fuzz-batch-a

Conversation

@ryansolid

@ryansolid ryansolid commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

Five rule-decided L2 fixes from GabbeV's semantic fuzzer (fuzz/semantic-fuzzer-l2), one commit per finding, following #3798's pattern: each pin in tests/fuzz-findings-l2.test.ts flips from it.fails to it under a behaviour name, the deciding rule is cited in source, and each commit has its own changeset.

F5 is not in this PR. It was planned as the sixth fix, but both candidate mechanisms introduce a runtime crash under the fuzzer (details below). Its pin stays it.fails. The title says five fixes instead of the planned six for that reason.

Findings

F10 / F11: a memo pulled by an isPending probe computes outside the probe's window. Commit e9e0e15.

  • Shape (F10): a gated isPending reader is revealed beside a write to the probed memo's source. The memo's plain reader stays on the old value for good.
  • Shape (F11): a pulled memo over an uninitialized staged input reads undefined instead of suspending.
  • Cause: verdictValue pulled m with the window's dispatch still installed. m's own reads went through verdictValue under the probe's posture. The unheld-staged arm answered them with the committed value, and m cached it for every reader.
  • Rules: A31 (a memo computes under its own lane posture, never its puller's), A28, A7 / A19 exc. 1.
  • Fix:
    • core/verdict.ts verdictValue: the nested pull runs with the dispatch dropped, and the dispatch is reinstalled after it.
    • latest / isPending: they restore the dispatch they found, so a window opened inside the pulled pass closes back to none. That case is pinned as "F10 (nested window)", a new it that fails on next.
    • setWindows is removed. It's internal.
  • Deviation: the brief's optional hardening of the unheld-staged arm for STATUS_UNINITIALIZED is not added. With the pull outside the window, no reachable path served undefined, so there was no failing case to pin.

F9: a verdict reader re-derives when its source settles silently. Commit a337a2a.

  • Shape: setSrc(1); setSrc(0) re-asks an async memo for the committed input. A probe-only isPending(m) reader stays true after the re-ask lands.
  • Cause: a landing equal to the committed value notifies nobody. The settle walk returned early at removePendingSource for the verdict reader, which holds no pending source of its own.
  • Rule: A19 (pending while any cause holds it, final the moment none does).
  • Fix: core/async.ts settlePendingSource. The settle closure's early return re-derives a CONFIG_VERDICT node it reaches. It is folded into the existing early-return condition, because a separate branch cost 25 B.

F13: a born-held effect re-run as lane work re-stages its value. Commit 74452b1.

  • Shape: a latest(source) render effect whose mount is withdrawn and restored across an action's ticks loses the action's final write.
  • Cause: recompute's lane arm wrote the effect's value into its private _value beside its born-held staging. The lane seam's commitPendingNode then applied the stale staging.
  • Rule: A29 (born held: staged into the transaction, committed with it).
  • Fix: core/core.ts recompute, lane arm. An effect carrying a staging re-stages, the same as the mainline arm already does.

F3: a landing re-judges the transactions parked at its seam. Commit 5478d5a.

  • Shape:
    1. A reader mounted while a flight is up is born held.
    2. Unmounting it in the same hold stages its removal, so it becomes a zombie that observes another flight.
    3. That observation blocks an unrelated write (src=0) in a newer transaction.
    4. The older hold lands, and its commits dispose the zombie. src=0 still never publishes.
  • Cause: the seam judges transactions newest first. src=0 is judged blocked before the landing that disposes the zombie, and nothing judges it again.
  • Rules: A29 ruling A; A15 Pending removal releases latest while its async reader is still visible #3463 (a zombie is live "until the commit that disposes it").
  • Fix: core/scheduler.ts GlobalQueue.settle. The landing loop restarts after every landing.
  • Deviation from the brief's triage:
    • The brief pointed at the disposeChildren predicate. As triaged, it is false here, because land already nulled the zombie's _x._transaction.
    • Dropping that gate does not fix F3. flush() recomputes scheduled right after settle(), so a schedule() from inside a landing is lost.
    • The brief's alternative, re-judging to a fixpoint, is the fix used here.

F4: an errored pass's unreached reads still hold their flights. Commit 0c93980.

  • Shape:
    1. A tuple render reader observes m0's flight for a=1.
    2. b=1 re-runs it, and the pass throws NotReady at m1 before reaching m0.
    3. The hold on a=1 lands, so A=1 shows beside a tuple still derived from a=0.
  • Cause: blockedBy counts only links of the reader's last pass (s._gen === r._depGen, the O3 "stopped reading" rule). An errored pass never reached m0, so its link counted as dropped.
  • Rules: A15; A30 (an errored pass keeps its full dependency list).
  • Fix: core/scheduler.ts blockedBy. Links past _depsTail count as live when the reader's pass errored (r._x._error != null). The blocked docstring now distinguishes this case from the unchanged-pass tails kept for Same-value branch switch leaves a synchronous memo inconsistent with its inputs #3469.

F5: stopped, not in this PR

Mechanism, confirmed as triaged:

  • A second guess on a shown optimistic lane re-asks the derivation chain.
  • d0's lane pass goes pending and lists the render reader on the lane through laneStage, without REACTIVE_LANE_DIRTY.
  • The settle walk then re-queues the reader outside the lane, and it publishes 2,1.

Two candidate fixes were tried:

  1. The brief's settle-walk marking: 77 B minified on the core floor, which is over budget.
  2. Marking in lanes.ts laneStage: a lane pass with no answer marks its node REACTIVE_LANE_DIRTY.

Both forms of the second fix crash under the fuzzer: 39 cases across the readiness, derived-readiness and optimistic-readiness cohorts, on both seeds. The error is TypeError: Cannot read properties of null (reading '_into'), raised from recompute → txOf. The trace:

  1. A render effect held by a transaction is taken over by a lane. list re-points _transaction at the lane, and CONFIG_HELD stays set ("held by a blocked lane").
  2. The new mark makes the effect's next pass a lane pass.
  3. That pass doesn't read the lane, so laneStage's leave path nulls the lane _transaction but keeps CONFIG_HELD.
  4. The following pass calls txOf(null).

Before the mark, a leaf never reached that leave path. #3794's own head replays 38 of the 39 cases with the same crash; its base replays 1 with an unrelated error.

Two narrowings were tried, and neither works:

A correct fix has to change how a lane takes over a held leaf. That is beyond F5's triaged mechanism and its 20 B budget, so F5 stays pinned and #3766 stays open here.

Fuzzer before/after

Settings: --cases 1000 --shrink. "Before" is origin/next at b0bad02; "after" is this branch's head. Each cell shows failing cases before → after.

cohort seed 3289 seed 91501
latest 90 → 90 77 → 78
readiness 41 → 0 43 → 0
derived-readiness 41 → 0 43 → 0
optimistic-readiness 0 → 0 3 → 2
ordinary 1 → 1 0 → 0

No new signatures appear. Compared case by case, every remaining failure is one the baseline already had, with the same signature, except one.

That exception is seed 91501, latest case 827, "S1: Torn tuple delivered to an effect", which newly fails:

  • Bisecting puts its first failure at F9 (a337a2a). It replays clean at e9e0e15.
  • It needs a retaining Loading boundary. With boundary: "none" it replays clean.
  • The baseline already has this shape on both seeds (S1 torn tuple, retaining boundary, mount control). That is the F6 family: a retaining boundary hides the lane's reader from the blocker predicate.

It is not a new shape, so it is not pinned separately. Accepted by the maintainer (2026-10-05), to be verified by Batch C's F6 fix. F9's new re-derivation reaches the F6 tear in one more schedule. A hand translation of the case tears on next too, before F9, but it isn't a faithful F9-specific reduction.

The remaining failures fall in the open findings and their families: S2 mount control and S1 derivation/input (F1, F2, F8), S1 torn tuple under a boundary (F6), and the O1 optimistic publication case.

Tests

  • packages/signals, with the fuzzer directory excluded: 4934 passed, 8 expected fail, 2 skipped. On next it was 4924 passed, 12 expected fail.
  • Pins:
    • Flipped: F3, F4, F9, F10, F11, F13.
    • Added: "F10 (nested window)", plus the F13 pin in the first commit.
    • Still it.fails: F1, F2, F5, F6, F8.
  • packages/solid: 819 passed.
  • packages/web: 1139 passed, 1 expected fail.
  • node scripts/rules-index.mjs --check: current.

Public API changes

None. setWindows in core/verdict.ts was internal and is removed.

Re-pins

None. No existing pin's expectation changed in any commit.

Size

Each fix is within 20 B minified, measured on the core floor unless noted:

finding minified
F10/F11 +1 B (isPending/latest scenario only)
F9 +16 B
F13 +13 B
F3 +11 B
F4 +16 B

The batch totals +56 B minified on the core floor and +57 B on isPending/latest, within the 100 B batch budget.

Local size harness (scripts/size, brotli), next b0bad02 vs this branch:

scenario next brotli this PR brotli Δ brotli Δ minified cap over by
signals: core floor (createSignal/Memo/Effect/Root/flush) 7,319 B 7,338 B +19 B +56 B 7.33 KB 8 B
signals: + createStore 14,504 B 14,546 B +42 B +56 B 14.53 KB 16 B
signals: + isPending/latest 9,456 B 9,476 B +20 B +57 B 9.47 KB 6 B
app: render + one signal (the simple-app floor) 9,811 B 9,848 B +37 B +56 B 9.83 KB 18 B
app: hydrating (no stores) with Show/For/Loading/Errored/lazy 17,648 B 17,695 B +47 B +56 B 17.66 KB 35 B
app: hydrating + every store primitive family 28,736 B 28,829 B +93 B +56 B 28.80 KB 29 B
app: CSR with Show/For/Loading/Errored/lazy 12,815 B 12,847 B +32 B +56 B 12.82 KB 27 B
app: CSR, observe tier 14,388 B 14,446 B +58 B +56 B 14.39 KB 56 B
app: CSR, observe tier + attribution engine enabled 28,587 B 28,611 B +24 B +58 B 28.62 KB
frames: eager client consumer 13,770 B 13,770 B +0 B +0 B 13.78 KB
page: base server components 44,829 B 44,787 B -42 B +57 B 44.84 KB
page: live server components 48,454 B 48,495 B +41 B +59 B 48.47 KB 25 B
server: floor 1,331 B 1,331 B +0 B +0 B 1.34 KB
server: renderToString 20,412 B 20,412 B +0 B +0 B 20.42 KB

CI's size job (run 37352614179) matched the table above exactly. A clean rebuild after merging origin/next (c8aac88, compiler-only changes) measures the same bytes.

Size exception granted by the maintainer (2026-10-05) for the nine over-cap scenarios. Each cap is set at the CI-measured size + 10 B, rounded up to 0.01 KB, with a dated ledger note per raise in scripts/size/scenarios.js. The four floor caps live in floor-caps.json.

scenario cap before measured (CI) cap after
signals: core floor (floor-caps.json) 7.33 KB 7,338 B 7.35 KB
signals: + createStore 14.53 KB 14,546 B 14.56 KB
signals: + isPending/latest 9.47 KB 9,476 B 9.49 KB
app: simple-app floor (floor-caps.json) 9.83 KB 9,848 B 9.86 KB
app: hydrating, no stores (floor-caps.json) 17.66 KB 17,695 B 17.71 KB
app: hydrating + every store primitive family 28.80 KB 28,829 B 28.84 KB
app: CSR 12.82 KB 12,847 B 12.86 KB
app: CSR, observe tier 14.39 KB 14,446 B 14.46 KB
page: live server components (floor-caps.json) 48.47 KB 48,495 B 48.51 KB

Size-Exception: fuzz Batch A F3/F4/F9/F10-11/F13 (+56 B minified batch on the core floor), accepted by the maintainer 2026-10-05; nine caps raised to CI-measured + 10 B.


Not in scope and untouched: F1, F2 (Batch B), F6, F8, the adopted-staging case (Batch C), and #3796.

I checked #3796 with a signals-level reduction of its repro: a derived store with a local write, wrapped in an optimistic store and updated inside an action. During the pending action it shows Old / saving: false with the local write and New / saving: true without it. That is identical on next and on this branch, so none of these fixes changes its behaviour.

ryansolid and others added 6 commits October 5, 2026 09:39
… final write

Brings the F13 pin from fuzz/semantic-fuzzer-l2 (f3d6342) onto next,
beside #3798's flipped F7a/F7b/F12 pins: a `latest(source)` render
effect whose mount is withdrawn in the action's tick and restored in the
next is born held with its creation value; the body-end write re-runs it
in the verdict lane, and the landing then applies the stale born-held
staging over the lane's run. Pinned as `it.fails` (A29 creation-time
form, A28). No engine changes.

Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…obe's window (fuzzer F10, F11)

`verdictValue` links and pulls like a plain read, but it pulled with the
window's dispatch still installed: `pullComputed(m)` → `recompute(m)` →
`m`'s own reads went through `verdictValue` under the probe's posture.
For a source staged this flush and not held, the unheld-staged arm
answers the probe with the committed value — right for the probe, wrong
for `m`, whose result is cached for every reader. So `m` cached the
committed input and never got the flushed one (F10: a gated `isPending`
reader revealed beside a write to the probed memo's source left the
memo's plain reader on the old value for good), and a pulled memo over
an uninitialized staged input read `undefined` instead of suspending
(F11).

A31: "a memo computes under its own lane posture, never its puller's";
A28: a write is visible at flush to every channel; A7/A19 exc. 1: an
uninitialized input suspends. The pull now drops the dispatch for the
nested pass and reinstalls it after, so the probe's own read of `m`
answers the verdict for what `m` produced. `latest`/`isPending` restore
the dispatch they found instead of re-deriving it from their flags, so a
window opened inside the pulled pass closes back to none rather than to
the puller's (pinned: "F10 (nested window)"). `setWindows` goes away.

F10 and F11 flip from `it.fails` to `it`; the nested-window case is a new
`it` (fails on next). No other pin moved. Size: +1 B minified on the
isPending/latest scenario (+5 B brotli locally).

The brief's optional hardening of the unheld-staged arm for
STATUS_UNINITIALIZED was not added: with the pulled pass outside the
window, no reachable path served `undefined` from it in probing, so
there was no failing case to pin.

Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…ently (fuzzer F9)

A verdict reader holds no pending source of its own: when the source goes
pending, `propagateStatus`'s verdict arm re-derives it (CONFIG_VERDICT),
and it answers `isPending` true without inheriting the status. A flight
that then lands equal to the committed value notifies nobody — `setSignal`
skips `insertSubs` on an equal value — and the settle walk returned at
`removePendingSource` for a node without the source, so the probe read
`true` forever. Shape: `setSrc(1); setSrc(0)` re-asks an async memo for
the committed input; a probe-only `isPending(m)` reader stays true after
the re-ask lands.

A19: "a node is pending while any cause holds it and final the moment none
does". The settle walk's early return now re-derives a verdict reader it
reaches — the source settling is the reader's verdict changing, the third
transition beside `propagateStatus`'s pending and error arms. Folded into
the walk's existing early-return condition, which keeps the minifier's
single `if` (a separate branch split it and cost 25 B).

F9 flips from `it.fails` to `it`. No other pin moved. Size: +16 B
minified (core floor and every scenario carrying the settle walk).

Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…lue (fuzzer F13)

A `latest(source)` render effect whose mount is withdrawn in an action's
tick and restored in the next is born held: its creation value is staged
(`_pendingValue` 0) and its first run is the commit's (A29). The action's
final write re-runs it as the verdict lane's work, and `recompute`'s lane
arm wrote an effect's value into its private `_value` (1) — beside the
born-held staging. The lane's seam then committed the node:
`commitPendingNode` applied the stale staging (0) over the lane's run and
queued the effect's run with it, so the committed truth never showed.

A29 creation-time form ("staged into it, committed with it");
`recompute`'s own note: "an effect still carrying an uncommitted staged
value re-stages: the commit applies the latest pass, not the born-held
one". The lane arm now does the same for an effect carrying a staging; an
effect with none keeps writing its private slot for the lane's run.

F13 flips from `it.fails` to `it`. No other pin moved. Size: +13 B
minified (core floor).

Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
… (fuzzer F3)

A reader mounted while a flight is up is born held (A29 ruling A);
unmounting it in the same hold stages its removal, making it a zombie
that observes another flight, and that observation blocks an unrelated
write (`src=0`) in a newer transaction (A15 #3463: a zombie is live for
every hold "until the commit that disposes it"). The seam judges
transactions newest first: the `src=0` transaction is judged blocked,
then the older hold lands (its mount control nets to committed) and its
commits dispose the zombie — but nothing judges `src=0` again. The
`schedule()` in `disposeChildren` cannot cover this: `land` has already
nulled the zombie's `_x._transaction`, and even ungated, a `schedule()`
inside `settle` is overwritten by `flush`'s own `scheduled` recompute.

`settle` now restarts its landing loop after every landing, so the
transactions it parked are judged against the world the landing
committed. The zombie is disposed, holds nothing, and `src=0` lands at
the same seam.

Deviation from the hand-off brief's triage: the brief named the
`disposeChildren` predicate (zombie's transaction nulled before the
disposing commit) as the cause and offered "schedule() for any dying
pending node" or "iterate settle to a fixpoint". The predicate is false
as triaged, but dropping it does not fix F3 (the scheduled seam is lost
inside `settle`); the fixpoint is the fix. +11 B minified (core floor).

F3 flips from `it.fails` to `it`. No other pin moved.

Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…hts (fuzzer F4)

A tuple render reader observes `m0`'s flight for `a=1` (the hold on
`a=1`). A later `b=1` re-runs it and the pass throws NotReady at `m1`
before reaching `m0`. `blockedBy` counts only the reader's reads of its
last pass (`s._gen === r._depGen` — O3, #3494: a reader that stopped
reading releases the flight), so the unreached `m0` link counted as
dropped, the hold on `a=1` landed, and `A=1` showed beside a tuple still
derived from `a=0` while `m0`'s flight was in the air.

A15: writes whose async work is observed by a shared reader settle as
one unit; A30: an errored pass (a throw, NotReady included) keeps its
full list. An errored pass did not stop reading — it never got there.
`blockedBy` (scheduler.ts) now counts the links past `_depsTail` as live
when the reader's pass errored (`r._x._error != null`). The unchanged-
pass tails kept for A30 (#3469) are untouched: their pass completed.

+16 B minified (core floor).

F4 flips from `it.fails` to `it`. No other pin moved.

Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@changeset-bot

changeset-bot Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a1f49e7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@solidjs/signals Patch
test-integration Patch
@solidjs/web Patch
@solidjs/babel-plugin Patch
@solidjs/compiler Patch
@solidjs/diagnostics Patch
@solidjs/element Patch
@solidjs/h Patch
@solidjs/html Patch
solid-js Patch
@solidjs/universal Patch
todos-server-example Patch

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

@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Size (brotli, eager entry chunk)

scenario head vs base cap lazy chunks (not counted)
signals: core floor (createSignal/Memo/Effect/Root/flush) 7.34 KB +19 B (+0.3%) 7.35 KB ✅
signals: + createStore 14.55 KB +42 B (+0.3%) 14.56 KB ✅
signals: + isPending/latest 9.48 KB +20 B (+0.2%) 9.49 KB ✅
app: render + one signal (the simple-app floor) 9.85 KB +37 B (+0.4%) 9.86 KB ✅
app: hydrating (no stores) with Show/For/Loading/Errored/lazy 17.70 KB +47 B (+0.3%) 17.71 KB ✅ lazy-page.js 0.04 KB
app: hydrating + every store primitive family 28.83 KB +93 B (+0.3%) 28.84 KB ✅ lazy-page.js 0.04 KB
app: CSR with Show/For/Loading/Errored/lazy 12.85 KB +32 B (+0.2%) 12.86 KB ✅ lazy-page.js 0.04 KB
app: CSR, observe tier (same app on the observe artifacts) 14.45 KB +58 B (+0.4%) 14.46 KB ✅ lazy-page.js 0.04 KB
app: CSR, observe tier + attribution engine enabled 28.61 KB +24 B (+0.1%) 28.62 KB ✅ lazy-page.js 0.04 KB
frames: eager client consumer (frames client + transport, lazy codec) 13.77 KB 0 B 13.78 KB ✅
page: base server components (hydrating + dynamic + frames + sf reference) 44.79 KB −42 B (−0.1%) 44.84 KB ✅ decode.js 6.07 KB, lazy-page.js 0.04 KB
page: live server components (base + live/GET + action + isPending/latest) 48.49 KB +41 B (+0.1%) 48.51 KB ✅ decode.js 6.07 KB, lazy-page.js 0.04 KB
server: floor (getRequestEvent + isServer) 1.33 KB 0 B 1.34 KB ✅
server: renderToString (the server-render floor) 20.41 KB 0 B 20.42 KB ✅

Bundled with Rolldown (what Vite ships), brotli q11, decimal KB. Caps in scripts/size/scenarios.js; the floor and page caps in floor-caps.json are frozen (lower only, or Size-Exception: in the PR body).

@coveralls

coveralls commented Oct 5, 2026 •

Copy link
Copy Markdown

Coverage Report for CI Build 37357446407

Warning

Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes.
Quick fix: rebase this PR. Learn more →

Coverage remained the same at 75.991%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 1195
Covered Lines: 962
Line Coverage: 80.5%
Relevant Branches: 925
Covered Branches: 649
Branch Coverage: 70.16%
Branches in Coverage %: Yes
Coverage Strength: 27.96 hits per line

💛 - Coveralls

@codspeed

codspeed Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Merging this PR will improve performance by 11.28%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 2 improved benchmarks
✅ 186 untouched benchmarks

Performance Changes

Benchmark BASE HEAD Efficiency
⚡ projection derive: write one NESTED field (reference) 2.3 ms 2 ms +14.58%
⚡ memo + sync render effect only (reference) 32 ms 29.6 ms +8.08%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing fix/fuzz-batch-a (a1f49e7) with next (c8aac88)

Open in CodSpeed

ryansolid and others added 2 commits October 5, 2026 11:32
Accepted by the maintainer (2026-10-05). Each cap is set at the CI-measured
size + 10 B, rounded up to 0.01 KB, with a dated ledger note per raise.
Fuzz Batch A (F3/F4/F9/F10-11/F13) costs +56 B minified on the core floor;
brotli layout puts nine scenarios over:

- core floor 7.33 -> 7.35 KB (floor-caps.json)
- + createStore 14.53 -> 14.56 KB
- + isPending/latest 9.47 -> 9.49 KB
- simple-app floor 9.83 -> 9.86 KB (floor-caps.json)
- hydrating 17.66 -> 17.71 KB (floor-caps.json)
- hydrating + stores 28.80 -> 28.84 KB
- CSR 12.82 -> 12.86 KB
- CSR observe tier 14.39 -> 14.46 KB
- live server components 48.47 -> 48.51 KB (floor-caps.json)

Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants