How to Prevent Score Changes Across Different React-Doctor Version Upgrades

Pin your React-Doctor and OXlint versions, run scans in offline mode, and freeze configuration files to ensure deterministic project scores across version upgrades.

React-Doctor is a diagnostic tool from millionco/react-doctor that calculates project health scores based on linting rules and configurable penalties. While the scoring algorithm itself is a pure function of diagnostics and constants, version upgrades can introduce score variations due to evolving rule sets, updated penalty values, or changes to the remote scoring API. Understanding how to lock these variables ensures that your scores remain stable and comparable over time.

Why React-Doctor Scores Change Between Versions

React-Doctor computes scores through a four-stage pipeline that introduces multiple variability points. First, the discovery phase loads linters and configuration files. Second, OXlint (or ESLint) generates diagnostics based on the current rule set. Third, the local scoring algorithm in packages/react-doctor/src/utils/calculate-score-locally.ts calculates penalties using constants defined in packages/react-doctor/src/constants.ts. Finally, an optional API fallback in packages/react-doctor/src/utils/try-score-from-api.ts may override local calculations with server-side values.

Any upgrade can modify the OXlint rules, adjust penalty constants like ERROR_RULE_PENALTY (1.5) or WARNING_RULE_PENALTY (0.75), or change the remote API algorithm, resulting in different scores for identical codebases.

Strategies to Prevent Score Variations

Pin the Exact React-Doctor Version

Lock your dependency to a specific version in package.json to guarantee identical rule implementations and bundled OXlint versions across all environments.

{
  "dependencies": {
    "react-doctor": "0.12.3"
  }
}

Avoid using caret (^) or tilde (~) ranges, as these allow automatic installation of newer versions that may contain updated rules or scoring logic.

Run in Offline Mode

Pass the --offline flag to bypass the remote scoring API entirely. In offline mode, React-Doctor calls calculateScoreLocally directly, eliminating variability from network latency, API versioning, or server-side algorithm changes.

import { scan } from "react-doctor";

const diagnostics = await scan({ cwd: process.cwd(), offline: true });

This ensures the score depends solely on the diagnostics generated by the bundled linter and the local penalty constants.

Lock the OXlint Engine Version

OXlint is the underlying linter that produces diagnostics. Pin its version using pnpm.overrides in your root package.json to ensure the same rule set and penalty values are used even if upstream releases new rules.

{
  "pnpm": {
    "overrides": {
      "oxlint": "1.63.0"
    }
  }
}

According to the millionco/react-doctor source code, this override ensures that packages/react-doctor/src/utils/calculate-score-locally.ts processes identical diagnostic types regardless of React-Doctor upgrades.

Freeze Rule-Penalty Constants

The score formula multiplies distinct error and warning rule counts by constants defined in packages/react-doctor/src/constants.ts. Keep these values unchanged to guarantee identical numeric outputs:

  • ERROR_RULE_PENALTY: 1.5
  • WARNING_RULE_PENALTY: 0.75

These constants directly influence the final score calculation at lines 41-44 of the constants file.

Use a Stable Configuration File

The lint rules applied to your project are driven by configuration files such as oxlintrc.json and eslintrc.json. Store these files in version control and avoid automatic config migrations to prevent inadvertent rule additions or removals that would alter diagnostic counts.

Cache Diagnostics for Reproducibility

Because the score only depends on the diagnostic list, you can cache the JSON report generated by packages/react-doctor/src/utils/build-json-report.ts. Re-using this cached report with the same React-Doctor version yields identical scores even if the codebase changes later.

import { readFile } from "fs/promises";
import { calculateScoreLocally } from "react-doctor/src/utils/calculate-score-locally.js";

const report = JSON.parse(await readFile("./react-doctor-report.json", "utf8"));
const { score, label } = calculateScoreLocally(report.diagnostics);

Avoid API-Driven Scoring

The remote API (SCORE_API_URL in packages/react-doctor/src/constants.ts) returns scores that may differ if the backend algorithm changes. Force local scoring by disabling the API call through the offline flag or by ensuring the tryScoreFromApi function in packages/react-doctor/src/utils/try-score-from-api.ts falls back to local calculation.

Summary

  • Lock versions: Pin react-doctor and oxlint in package.json and pnpm.overrides.
  • Run offline: Use --offline to bypass the remote API and use local scoring.
  • Freeze constants: Maintain stable values for ERROR_RULE_PENALTY and WARNING_RULE_PENALTY.
  • Control configuration: Version-control your oxlintrc.json files and disable automatic migrations.
  • Cache results: Store diagnostic JSON reports to recompute identical scores later.

Frequently Asked Questions

Why does my React-Doctor score change after upgrading?

Scores change because version upgrades may include updated OXlint rules, modified penalty constants in packages/react-doctor/src/constants.ts, or algorithm changes in the remote scoring API. Each of these affects the diagnostic count or the weighting applied to errors and warnings.

How do I guarantee the same score across CI and local environments?

Pin the exact React-Doctor version in package.json, lock the OXlint version via pnpm.overrides, and run all scans with the --offline flag. This ensures both environments use identical rule sets and the local scoring algorithm from packages/react-doctor/src/utils/calculate-score-locally.ts.

Can I use the remote API and still get stable scores?

No. The remote API endpoint defined in SCORE_API_URL can return different scores if the server-side algorithm evolves. To prevent score changes across different react-doctor version upgrades, disable API calls and rely on the deterministic local scoring function.

What constants determine the final score label?

The thresholds in packages/react-doctor/src/constants.ts map numeric scores to labels: SCORE_GOOD_THRESHOLD (75) qualifies as "Great", and SCORE_OK_THRESHOLD (50) qualifies as "Needs work". Scores below 50 are labeled "Critical". Freeze these thresholds in your documentation to ensure consistent interpretations across versions.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →