Skip to content

fix(signals): a run that committed undefined is not WASTED_RECOMPUTE - #3716

Merged
ryansolid merged 2 commits into
solidjs:nextfrom
drakeo338:claude/3715-fix
Sep 30, 2026
Merged

ryansolid merged 2 commits into
solidjs:nextfrom
drakeo338:claude/3715-fix

Conversation

@drakeo338

@drakeo338 drakeo338 commented Sep 30, 2026 •

Copy link
Copy Markdown

Fixes #3715.

Summary

The WASTED_RECOMPUTE diagnostic counted a re-run as wasted whenever the computed's committed value was unchanged. A projection's internal computed always commits undefined (its work is draft writes or reconciling a returned value), so every projection re-run, and any memo that works by writing, was reported as producing the same value. The change passes a noValue flag into the check and skips the wasted count for runs that committed undefined.

How did you test this change?

Added attribution-wasted-recompute.test.ts with three cases (draft mutation, returned value to reconcile, memo that writes). Before the fix 3 of 8 tests fail; with it all 8 pass.


Maintainer addition (commit 7d723f8, pushed on top of the contributor's commit):

Maintainer follow-up

The undefined exemption now applies to changed itself in recomputeEnd, mirroring the existing effect-output rule, instead of a separate noValue flag read only by checkWastedRecompute. Every consumer of the per-run record now agrees with the finding. The draft-projection test also asserts that its rerun records report changed: true and that costs().wastedMs is 0.

Public API Changes

  • RerunEvent.changed is now true for a plain run that commits undefined (projections, memos that do their work by writing). This moves every consumer of that field:
    • costs().scopes[].wastedMs no longer counts these runs;
    • expectNoWaste in @solidjs/diagnostics no longer fails on them;
    • the performance tracks no longer colour them as unchanged / wasted, and the why-run log no longer marks them unchanged.
  • WASTED_RECOMPUTE no longer fires for these runs.

This matches the documented behavior (documentation/solid-2.0/08-dev-diagnostics.md, WASTED_RECOMPUTE: "a side-effect-only compute (undefined output) reports changed: true and is exempt"). No new exports, options or diagnostic codes.

A projection that mutates its draft, or a memo that does its work by
writing a signal, commits undefined on every run, so the equality gate
always closed and the re-runs were counted as wasted. The rule that
undefined outputs are exempt only applied to effects.

recomputeEnd now passes a separate noValue flag to checkWastedRecompute;
changed, costs and records are unchanged.

Fixes solidjs#3715
@changeset-bot

changeset-bot Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7d723f8

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

…consumer agrees

The exemption for runs that commit `undefined` lived only inside
checkWastedRecompute, so the RerunEvent still said `changed: false` and
costs().wastedMs, expectNoWaste, the performance tracks and the why-run
log kept counting projection and writing-memo runs as waste. Apply it to
`changed` in recomputeEnd instead, mirroring the effect-output rule, and
drop the noValue parameter. This is the documented behavior: an
`undefined`-output compute reports `changed: true` and is exempt.

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

codspeed Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 185 untouched benchmarks
⏩ 3 skipped benchmarks1


Comparing drakeo338:claude/3715-fix (7d723f8) with next (da84bd9)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@ryansolid

Copy link
Copy Markdown
Member

Thanks @drakeo338 — the diagnosis in #3715 was spot on, and the tests (including the constant-memo control) made this easy to finish.

We pushed one follow-up commit on top of yours (7d723f8) rather than asking for another round:

  • The exemption moved onto changed itself. With the separate noValue flag, only WASTED_RECOMPUTE skipped these runs; the per-run record still said changed: false, so costs().wastedMs, expectNoWaste in @solidjs/diagnostics, the performance tracks and the why-run log kept counting every projection run as waste — and the finding disagreed with costs() about the same runs. recomputeEnd now reports a run that committed undefined as changed, mirroring the existing effect-output rule, so every consumer reads the same fact. This is what 08-dev-diagnostics.md already documents ("a side-effect-only compute (undefined output) reports changed: true and is exempt").
  • One added assertion: the draft-projection case now also checks that its rerun records report changed: true and that costs().wastedMs is 0.

We accepted the blind spot you'd expect from this rule: a memo that's genuinely stuck at undefined is no longer flagged. We looked for a precise signal instead — a "this run wrote" marker catches writing memos and draft writes, but a returning projection's reconcile lands after its run has ended, and writes to unread store keys fire no hook, so it can't cover projections without after-the-fact accounting. Identity of undefined proving nothing is already the rule for effects, so we kept it.

This lands in the next release, 2.0.0-rc.14.

— Claude via Cursor

@ryansolid
ryansolid merged commit b0c8489 into solidjs:next Sep 30, 2026
6 of 7 checks passed
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