Fix AtomRegistry missed updates through stale nodes and leaked superseded builds - #8543
Merged
tim-smart merged 7 commits intoSep 28, 2026
Merged
Conversation
front-depiction
requested a deployment
to
fork
September 25, 2026 20:19 — with
GitHub Actions
Waiting
front-depiction
had a problem deploying
to
fork
September 25, 2026 20:19 — with
GitHub Actions
Error
🦋 Changeset detectedLatest commit: a1d42c8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 31 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 |
Contributor
Bundle Size AnalysisGenerated from PR build output; treat the content below as untrusted.
|
front-depiction
force-pushed
the
atom-registry-dependency-relation
branch
from
September 25, 2026 20:36
263aa59 to
1c01832
Compare
front-depiction
requested a deployment
to
fork
September 25, 2026 20:36 — with
GitHub Actions
Waiting
front-depiction
had a problem deploying
to
fork
September 25, 2026 20:36 — with
GitHub Actions
Error
front-depiction
force-pushed
the
atom-registry-dependency-relation
branch
from
September 25, 2026 20:37
1c01832 to
0176431
Compare
front-depiction
requested a deployment
to
fork
September 25, 2026 20:37 — with
GitHub Actions
Waiting
front-depiction
had a problem deploying
to
fork
September 25, 2026 20:37 — with
GitHub Actions
Error
front-depiction
marked this pull request as ready for review
September 25, 2026 20:39
front-depiction
force-pushed
the
atom-registry-dependency-relation
branch
from
September 26, 2026 15:14
0176431 to
2a9c6fb
Compare
front-depiction
requested a deployment
to
fork
September 26, 2026 15:15 — with
GitHub Actions
Waiting
front-depiction
had a problem deploying
to
fork
September 26, 2026 15:15 — with
GitHub Actions
Error
front-depiction
requested a deployment
to
fork
September 27, 2026 17:13 — with
GitHub Actions
Waiting
front-depiction
had a problem deploying
to
fork
September 27, 2026 17:14 — with
GitHub Actions
Error
tim-smart
force-pushed
the
atom-registry-dependency-relation
branch
from
September 27, 2026 21:51
065ca04 to
bd507cd
Compare
…eded builds The registry dropped the edges from a parent to its dependents as soon as the parent changed, and expected each dependent to add them back when it rebuilt. A dependent that stays stale never does: its parents could be released under it, and their next change never reached it. A build that wrote to one of its own inputs was rebuilt inside itself, and the outer build's lifetime was never disposed. Edges are now kept while a node is stale. A parent's change is pushed only to the children that depend on its current value, valid ones and running builds that already read it, and a build drops the parents it did not read when it returns. A stale node is brought up to date when it gains a listener. A build superseded by a nested rebuild is disposed, and a node rebuilds until it is valid. Observed dependents are counted instead of walking the children on every invalidation. A batch drains even when its function throws, and finalizers, listeners and idle sweeps run to completion when one of them throws.
tim-smart
force-pushed
the
atom-registry-dependency-relation
branch
from
September 27, 2026 22:49
4eefb05 to
bf4c921
Compare
A build that threw left its node stale with no way back: a later change in a parent skipped it as already stale, so a subscribed node never heard another update, and a dependent kept a value the failed node no longer stood behind. A source also skipped its own listeners when a dependent's rebuild threw. A build that throws now stales the dependents that hold or are reading its value, and an observed node that stayed stale this way is rebuilt again when a parent commits. The edge to a parent is kept even when the parent could not produce a value, so its recovery reaches the reader. A node that pulls a parent which produced its value but failed to update another dependent still takes the value and rethrows once its own build is done. A source announces its own change before rethrowing a dependent's failure.
One predicate decides which dependents a parent's change reaches, and it includes the retry of an observed dependent whose rebuild failed, so a failed build reuses the same propagation as a commit. A tracked read records itself in one place once the parent has answered. Committing a value no longer special-cases the first value: invalidating dependents returns early without children, and listeners are announced only when there are some.
A value notifies the listeners present when it is committed. That was already so for a changed value, and the previous commit made it so for a first value; an initial value set before its first build now follows the same rule instead of queueing a notification for listeners a batch adds later.
kriegcloud
added a commit
to beep-effect/beep-effect
that referenced
this pull request
Sep 28, 2026
…gistry The snapshot's AtomRegistry.reset empties the node map before running node finalizers (Effect-TS/effect#8543), so the graph-worker finalizer's registry.get on the watchdog/boundary/failure request atoms created nodes on a disposed registry and threw "registry is disposed" in three Session atom tests. Cancellation now bumps a request counter only while its atom still has a node; a request nobody can answer needs no cancellation. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
kriegcloud
added a commit
to beep-effect/beep-effect
that referenced
this pull request
Sep 28, 2026
… 1) (#1310) * chore(deps): bump the Effect catalog to snapshot e5f7d12af9 (rc.118 + 1) Move every Effect catalog entry from the pkg.pr.new snapshot of main 330b7475e2 to the snapshot of e5f7d12af9 (one commit after the 4.0.0-rc.118 tag). The Bun patches for effect and platform-node-shared are re-keyed to the new URLs (upstream sources unchanged), the OSV override is re-keyed, and the effect-vitest primitives graph, charter fixtures, and inventory are re-pinned to the @effect/vitest@4.0.0-rc.118 tag (101 entries: the README gained a fixtures section). HttpRouter.serve now owns the router it serves (Effect-TS/effect#8477), so the professional-desktop sidecar builds its rpc server inside the served app instead of as a sibling of it; the previous shape registered the handlers on an unserved second protocol. The integration harness follows the same shape. The knowledge command-surface parser drops the pre-rc.118 effect/unstable/cli specifier. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(ontology-client): skip request cancellation on a disposed atom registry The snapshot's AtomRegistry.reset empties the node map before running node finalizers (Effect-TS/effect#8543), so the graph-worker finalizer's registry.get on the watchdog/boundary/failure request atoms created nodes on a disposed registry and threw "registry is disposed" in three Session atom tests. Cancellation now bumps a request counter only while its atom still has a node; a request nobody can answer needs no cancellation. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * test(epistemic-server): count SHACL violations without spying on the schema The snapshot caches every schema's `make` as a lazily built, non-configurable own property (Effect-TS/effect internal/schema/make.ts), so `vi.spyOn(ShaclValidationViolation, "make")` throws "Attempting to change value of a readonly property" once any test has built a violation. The bounded-generation test now counts constructions through a module mock of the layer's import, whose wrapper inherits the schema and shadows only `make`. `vi` comes from `vitest` because only that import gets its `mock` and `hoisted` calls hoisted; the effect-vitest inventory records the three plain-vitest rows. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This branch is waiting to be deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
AtomRegistrydropped the edges from a parent to its dependents as soon as the parent changed, and expected each dependent to add them back when it rebuilt. A dependent that stays stale never does. Its parents could be released under it (akeepAlivederived atom lost the state it read), and a change in one of those parents never reached it again, even after a listener was added. A build that wrote to one of its own inputs was rebuilt inside itself, and the outer build's lifetime was never disposed.This revision replaces the earlier rewrite in this PR with a smaller change to the registry on
main:Atom.tsis unchanged. Four dependency/failure regressions and three batch-notification cases live inpackages/effect/test/reactivity/Atom.test.ts. The original three fail onmain; the fourth covers recovery after a derived build throws and verifies that the source listener is still notified when its dependent fails. The batch cases cover a late immediate subscriber after a first value, after a first value queued with an existing listener, and afterinitialValuesis applied in a batch.Behaviour changes worth knowing
subscribewithoutimmediatebrings a stale node up to date, so it can throw where it previously could not.immediate: truesubscription. Onmain, a first value committed inside a batch was queued even without listeners, so a late subscriber heard it, or heard it twice, where a changed value would have stayed silent.Validation
With the three batch-notification cases, the reactivity suite passes (134), as do
pnpm checkandpnpm lint. Before those cases were added, reactivity, hydration, React, Solid and Vue tests passed (296).A randomized check drove random graphs of state and derived atoms (conditional reads, builds that throw for some inputs,
keepAlive, non-lazy and idle-TTL variants) through sets, batches, reads, subscriptions with listeners that write back directly or in a nested batch, refreshes and scheduler flushes, comparing every value, edge and observed count against a pure model, requiring observed nodes to recover from failed builds, and checking that nothing leaks once released. It passes 100000 seeds here.mainfails within the first ten, and the previous revision of this PR failed on nested rebuilds staled by a listener write.Median ns per operation, each case in its own process:
mainCloses EFF-1604