Skip to content

docs(signals): the direction rule — a hold never waits on work that has never committed - #3820

Merged
ryansolid merged 2 commits into
nextfrom
docs/direction-rule
Oct 6, 2026
Merged

ryansolid merged 2 commits into
nextfrom
docs/direction-rule

Conversation

@ryansolid

Copy link
Copy Markdown
Member

Adds the direction rule to the async SPEC as a dated amendment (2026-10-06): a hold never waits on work that has never committed. Docs and tests only; no source changes, so no changeset.

Why

A structural review of the post-L2 fixes found the concept they were missing: direction. L2 has one relation between transactions, merge, which is symmetric, so "this work read that hold" means both wait for each other. That's right for committed work. It's wrong for work that has never committed (a node mounted during a hold, its first load): that work should wait for the hold, and the hold should not wait for it. Each create-time fix said this locally by checking STATUS_UNINITIALIZED. The amendment names the rule and points at those mechanisms; it adds none.

A carve that made direction a transaction-level relation (a frame follows a hold) was built and measured on a scratch branch. It is not adopted:

What the amendment says

Pins

packages/signals/tests/direction-rule-probe.test.ts:

Shape On next
same tick, two mounts outside a flush it.fails
same tick, a mount then a sync reader it.fails
in a flush that joined the hold it.fails
in a flush, under a Loading that has not shown content passes (normal test)

The three it.fails rows pass under the measured follow model, so they will flip loudly if a fix makes those shapes one-way. They also stay failing with #3800 and #3540 applied, which is consistent with the amendment.

Also:

  • One-sentence cross-references in INTERNALS-ASYNC-STATE.md §0 and the carve plan's §15 open question 4.
  • RULES-INDEX.md regenerated; rules-index --check is clean.

Public API changes

None.

…as never committed

A dated SPEC amendment (2026-10-06) naming the rule the create-time fixes
each implement by checking commit status: the loading-source rule, #3814
(#3802), and the pending #3800 and in-flush #3540 fixes. The same-tick
and in-flush shapes are recorded as not yet one-way and pinned it.fails
in tests/direction-rule-probe.test.ts; the boundary shape passes today.
Cross-references in INTERNALS-ASYNC-STATE.md §0 and the carve plan.

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

changeset-bot Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: b4f9236

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions

github-actions Bot commented Oct 6, 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.35 KB 0 B 7.35 KB ✅
signals: + createStore 14.53 KB 0 B 14.56 KB ✅
signals: + isPending/latest 9.49 KB 0 B 9.49 KB ✅
app: render + one signal (the simple-app floor) 9.85 KB 0 B 9.86 KB ✅
app: hydrating (no stores) with Show/For/Loading/Errored/lazy 17.67 KB 0 B 17.71 KB ✅ lazy-page.js 0.04 KB
app: hydrating + every store primitive family 28.80 KB 0 B 28.87 KB ✅ lazy-page.js 0.04 KB
app: CSR with Show/For/Loading/Errored/lazy 12.83 KB 0 B 12.86 KB ✅ lazy-page.js 0.04 KB
app: CSR, observe tier (same app on the observe artifacts) 14.39 KB 0 B 14.46 KB ✅ lazy-page.js 0.04 KB
app: CSR, observe tier + attribution engine enabled 28.64 KB 0 B 28.66 KB ✅ lazy-page.js 0.04 KB
app: compiled floor (one template, one text hole, one delegated click) 10.03 KB 0 B 10.05 KB ✅
app: compiled CSR (JSX todo app: spread/merge/omit, events, class/style, keyed For, Show, Loading + lazy, store) 25.11 KB 0 B 25.13 KB ✅ stats.js 0.18 KB
app: compiled hydrating (the same JSX todo app through hydrate(), compiled hydratable) 30.92 KB 0 B 30.93 KB ✅ stats.js 0.20 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.80 KB 0 B 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 0 B 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

Copy link
Copy Markdown

Coverage Report for CI Build 37421040388

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.79 hits per line

💛 - Coveralls

@ryansolid
ryansolid merged commit 79df376 into next Oct 6, 2026
6 checks passed
@codspeed

codspeed Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 188 untouched benchmarks


Comparing docs/direction-rule (b4f9236) with next (076a250)

Open in CodSpeed

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