Skip to content

Make the latest() shadow computed purely write-driven #2838

Description

@ryansolid

Background

_latestValueComputed (the shadow node behind latest()) is currently both recompute-driven and write-driven: it is an optimisticComputed(() => read(el)) that reacts through the graph, and every write path (setSignal, async resolution, transition-held sync recompute) force-writes it via syncCompanions().

This dual nature was the root cause of the #2829/#2831 bug cluster — the shadow recomputing under whatever ambient stale/lane posture happened to be active is why the following patches exist:

  • the _parentSource !== el exemption in read()'s value-selection ternary
  • the STATUS_UNINITIALIZED clearing in resolveOptimisticNodes
  • the NotReadyError catch in read()'s latest branch

Each is correct, but they are all compensations for the same underlying tension.

Proposal

Make the shadow purely write-driven: a plain optimistic signal fed exclusively by syncCompanions(), never recomputing on its own. The "shadow recomputed under the wrong context" bug class disappears structurally, and the read-path exemptions above should become deletable.

Costs to account for:

  • The shadow's compute currently provides initialization (first latest() read) and NotReady propagation (the shadow's own STATUS_PENDING is what isPending(() => latest(x)) keys off). Both need explicit handling in the write/status paths instead.
  • The parent-child lane machinery in lanes.ts (_parentLane, the independence rules in assignOrMergeLane) exists largely to serve the companions and should shrink as part of this.

Timing

Beta-period work, targeted after the upcoming beta release — the area just stabilized against three real-world reports, and the regression coverage in packages/solid-signals/tests/latest-isPending-consistency.test.ts and tests/latest-async.test.ts is what makes this refactor safe to attempt.

Refs #2829, #2831.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions