Skip to content

feat(core): CORRECT004 — unmutated $state → const - #78

Merged
oekazuma merged 9 commits into
mainfrom
feat/correct004-unmutated-state
Jul 2, 2026
Merged

oekazuma merged 9 commits into
mainfrom
feat/correct004-unmutated-state

Conversation

@oekazuma

@oekazuma oekazuma commented Jul 2, 2026 •

Copy link
Copy Markdown
Owner

Another Correctness/reactivity rule from #69. Adds CORRECT004, flagging a let x = $state(...) that is never written or escaped anywhere in the component — its reactivity is unused, so const (or $state.raw if only reassigned wholesale) is clearer and cheaper. info severity.

Distinct from CORRECT002/003 (which are about $effect misuse); this is about $state that never changes.

What's flagged

let title = $state('Dashboard');   // ✗ flag ({title} read only) → const
let cfg = $state({ a: 1 });        // ✗ flag ({cfg.a} read only)
let n = $state(0); n++;            // ok (written)
<button onclick={() => n++}>       // ok (handler mutation)
<input bind:value={name}>          // ok (bind)
<Child d={data} />                 // ok (component prop escape)

How it works (precision-first — the cardinal sin is a false positive)

A new ComponentFacts.constableStates field, computed by the CLI parser. A $state is suppressed (not flagged) if anywhere it is:

  • reassigned / compound / ++/--, member/element assigned (x.a=), destructuring-assigned (({x} = o)), delete x.p;
  • the object of a method call (x.push()), or passed to any call (f(x), f(x.a));
  • bound (bind:value={x}) or passed as a component prop (<Child d={x}>, <svelte:component d={x}>).

Writes are detected over both the instance script AND the template AST — inline handlers like onclick={() => x++} live in the template, and missing them would be a false positive. Reads (interpolation, x.a reads, DOM attributes, component slot children) do not suppress.

The componentRule-built rule flags each constable state; it no-ops in rendered mode. stateNames/assignsOnlyState/reactiveNames (CORRECT002/003) are untouched.

Docs & release

  • 2 rule reference pages (CORRECT004, en + ja).
  • @svelte-vitals/core + svelte-vitals + @svelte-vitals/mcp minor (CLI/static rule; not @svelte-vitals/vite).

Validation

pnpm -r test (565: core 253 / vite 76 / cli 227 / mcp 9), pnpm -r typecheck, pnpm lint, pnpm --filter docs build (113 pages incl. the 2 new) — all green.

Process

spec → plan → subagent-driven (3 impl tasks, each spec + quality reviewed) + a whole-branch review on Opus. During planning I caught that inline-handler mutations live in the template AST (fixed the spec to walk both). The Opus review then found four more false-positive vectors (destructuring-assign — which produced build-breaking const advice — delete, member call-args, and dynamic-component props); all were fixed with regression tests before this PR. Every write/escape addition is strictly additive, so it can only ever produce a benign false negative, never a false positive.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a new correctness check for $state values that are never mutated or passed around, helping suggest simpler const usage.
    • Exposed new component fact data to support this check and made it available in rule listings.
  • Bug Fixes

    • Improved analysis of state usage across templates and scripts, including bindings, component props, and function calls.
  • Documentation

    • Added rule documentation in English and Japanese, plus supporting design/spec notes.

oekazuma and others added 8 commits July 2, 2026 16:57
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… too

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 2, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@oekazuma, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 37 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 052f90ef-b790-4c32-9c2a-cf6ba1cafbf8

📥 Commits

Reviewing files that changed from the base of the PR and between f828598 and 3d35554.

📒 Files selected for processing (2)
  • packages/cli/src/providers/source/parse.ts
  • packages/cli/test/parse-component-facts.test.ts
📝 Walkthrough

Walkthrough

Adds a new CORRECT004 correctness rule that flags $state declarations never written or escaped, recommending const or $state.raw. Extends ComponentFacts and the CLI parser with constableStates analysis, registers the rule in core, updates tests, and adds documentation/spec/plan/changeset files.

Changes

CORRECT004 rule implementation

Layer / File(s) Summary
ComponentFacts contract and CLI fallback
packages/core/src/component.ts, packages/cli/src/providers/source/components.ts
Adds constableStates: { name, line }[] to ComponentFacts and initializes it in the CLI parse error fallback.
CLI parser analysis
packages/cli/src/providers/source/parse.ts, packages/cli/test/parse-component-facts.test.ts
Adds AST-walking helpers to collect $state declarations and detect writes/escapes across script and template, deriving constableStates; adds corresponding parser tests.
Rule definition and registration
packages/core/src/rules/correctness/correct004-unmutated-state.ts, packages/core/src/rules/index.ts, packages/core/src/index.ts, packages/core/test/correctness-rules.test.ts
Implements correct004UnmutatedState rule with applies/bad logic, registers it in allRules and exports, and adds behavior tests.
Existing test fixture updates
packages/core/test/architecture-rules.test.ts, packages/core/test/bundle-rules.test.ts, packages/core/test/security-rules.test.ts
Updates existing ComponentFacts/RuleContext fixtures to include the new constableStates field.
Docs, spec, plan, changeset
docs/src/content/docs/rules/correct004.md, docs/src/content/docs/ja/rules/correct004.md, docs/superpowers/specs/..., docs/superpowers/plans/..., .changeset/correct004-unmutated-state.md
Adds English/Japanese rule docs, a design spec, an implementation plan, and a minor-version changeset entry.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI as parseComponentFacts
  participant Walker as AST Walkers
  participant Facts as ComponentFacts
  participant Rule as correct004UnmutatedState

  CLI->>Walker: collect $state declarations (stateDecls)
  CLI->>Walker: walk instance script + template for writes/escapes
  Walker-->>CLI: writtenOrEscaped set
  CLI->>CLI: filter stateDecls not in writtenOrEscaped
  CLI->>Facts: attach constableStates
  Facts->>Rule: provide constableStates to RuleContext
  Rule->>Rule: applies() checks constableStates.length > 0
  Rule-->>Facts: emit finding per constable state
Loading

Possibly related PRs

  • oekazuma/svelte-vitals#68: Both PRs extend the same parseComponentFacts/ComponentFacts analysis pipeline for correctness rules alongside existing CORRECT001/002 infrastructure.
  • oekazuma/svelte-vitals#70: Both PRs modify the shared ComponentFacts/parseComponentFacts schema, adding different rule-specific fields (constableStates vs htmlTags/javascriptUrls).
  • oekazuma/svelte-vitals#71: Both PRs extend the returned ComponentFacts shape with new analysis fields (constableStates vs loc/propCount) for different rules.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding CORRECT004 for unmutated $state and recommending const.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/correct004-unmutated-state

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new correctness rule CORRECT004 to identify $state declarations that are never mutated or escaped, so the tool can recommend const (or $state.raw for wholesale reassignment) and avoid unnecessary reactivity overhead. This fits the project’s “high-precision, deterministic Svelte code-health scanner” direction from #69 by expanding the Correctness/reactivity rule set.

Changes:

  • Introduces ComponentFacts.constableStates and populates it in the CLI parser by analyzing both instance script and template ASTs.
  • Adds the correct004UnmutatedState rule to @svelte-vitals/core, registers/exports it, and adds rule-level tests.
  • Adds CORRECT004 docs (en/ja), design/spec docs, and a changeset for minor releases.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/core/test/security-rules.test.ts Updates test helper ComponentFacts shape with constableStates.
packages/core/test/correctness-rules.test.ts Adds CORRECT004 tests and updates helper ComponentFacts shape.
packages/core/test/bundle-rules.test.ts Updates test helper ComponentFacts shape with constableStates.
packages/core/test/architecture-rules.test.ts Updates test helper ComponentFacts shape with constableStates.
packages/core/src/rules/index.ts Registers and re-exports correct004UnmutatedState in allRules.
packages/core/src/rules/correctness/correct004-unmutated-state.ts Implements CORRECT004 as a componentRule based on constableStates.
packages/core/src/index.ts Re-exports correct004UnmutatedState from the core package entrypoint.
packages/core/src/component.ts Extends ComponentFacts with constableStates.
packages/cli/test/parse-component-facts.test.ts Adds parser coverage for constable $state detection and suppression cases.
packages/cli/src/providers/source/parse.ts Implements collection of $state writes/escapes across script + template to compute constableStates.
packages/cli/src/providers/source/components.ts Ensures fallback ComponentFacts includes constableStates.
docs/superpowers/specs/2026-07-02-correct004-unmutated-state-design.md Documents CORRECT004 design and precision rules.
docs/superpowers/plans/2026-07-02-correct004-unmutated-state.md Documents implementation plan and validation steps for CORRECT004.
docs/src/content/docs/rules/correct004.md Adds English user-facing rule reference for CORRECT004.
docs/src/content/docs/ja/rules/correct004.md Adds Japanese user-facing rule reference for CORRECT004.
.changeset/correct004-unmutated-state.md Adds changeset for minor bumps across core/app/mcp.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/cli/src/providers/source/parse.ts Outdated
Comment thread packages/cli/src/providers/source/parse.ts
…-args (PR #78 review)

UpdateExpression now resolves its argument via rootObjectName so `s.n++` /
`s[i]++` mark the state written (not just bare `s++`); spread call arguments
`f(...s)` / `f(...s.items)` are unwrapped before rootObjectName. Both were
missed write/escape paths → false-positive CORRECT004 flags.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.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