Reference

The rules

Ten checks over one question: do the places this project declares a toolchain version agree with each other? Each rule names the sources it compares and carries a fixed code.

What a run reads

lockstep scans a single project directory. Every source below is optional; a missing file is skipped, not an error. Nothing is written back to the project.

  • package.json — engines.node, engines.bun, packageManager, and volta.
  • .nvmrc and .node-version — the Node version each manager would install.
  • .tool-versions and mise.toml — the asdf and mise pins.
  • The committed lockfile — package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lock, or bun.lockb.
  • Every .github/workflows/*.yml — setup-node, setup-bun, pnpm/action-setup, corepack, and whether the install is frozen.

The ten rules

Rules across Node drift, package-manager drift, lockfiles, and CI.
Code Severity Check
LOCK-001errorConflicting Node versions across sources
LOCK-002errorpackageManager conflicts with the committed lockfile
LOCK-003errorMultiple lockfiles from different managers
LOCK-004warningLockfile committed but CI does not install frozen
LOCK-005warningNo lockfile committed
LOCK-006warningpackageManager declared but CI never enables corepack
LOCK-007infoengines.node range does not include the CI-pinned Node
LOCK-008info.nvmrc and .node-version differ only cosmetically
LOCK-009warningCI bun-version major disagrees with engines.bun
LOCK-010infoNo GitHub Actions workflow found so CI cannot be verified

How the checks behave

LOCK-001 — conflicting Node versions

Every Node declaration is reduced to its major, and the majors must be a single value. Two sources claiming Node 18 and Node 20 is an error; a range such as >=18 is compared by the major it names.

LOCK-002 — packageManager versus the lockfile

The manager named in packageManager must own the committed lockfile. Declaring pnpm while committing a package-lock.json is an error, because the next contributor will install with a different manager than the one the project pins.

LOCK-003 — multiple lockfiles

More than one manager's lockfile in the tree is an error. Two lockfiles mean two resolution graphs, and whichever a tool happens to pick silently wins.

LOCK-004 — frozen installs

A committed lockfile is only authoritative if CI installs from it without rewriting it. When a lockfile exists and a workflow does not use npm ci, --frozen-lockfile, or --immutable, lockstep warns.

LOCK-005 — no lockfile

With no lockfile committed, dependency resolution is not pinned at all. This warns rather than errors, because some libraries intentionally ship without one.

LOCK-006 — corepack never enabled

A packageManager field does nothing unless a workflow activates it. lockstep warns when the field is present but no workflow enables Corepack or pins the same package-manager version through an action.

LOCK-007 — engines versus CI

When engines.node is a simple range, its major should include the Node version CI pins. A range that excludes the CI runtime is informational, because the two can drift without the build failing outright.

LOCK-008 — cosmetic difference

.nvmrc and .node-version that differ only by a v prefix or trailing whitespace still resolve to the same version. lockstep reports it so the two files can be made identical.

LOCK-009 — Bun version

When engines.bun and a workflow's bun-version disagree by major, lockstep warns. The same drift that applies to Node applies to Bun.

LOCK-010 — CI cannot be verified

With no .github/workflows/*.yml, lockstep cannot confirm how the project installs or which runtime it uses. It reports that as informational, so a green result is never mistaken for a verified one.

How a run is assembled

The pipeline is static. There is no rendering step and no network access.

  1. Scan

    Read package.json, the version files, the lockfiles, and the workflow directory, collecting each version declaration with the file it came from.

  2. Normalize

    Reduce each value to a comparable form: a major for runtimes, a manager name for lockfiles, and a pinned version for actions.

  3. Compare

    Run the ten rule functions over the collected facts. Each emits an issue with a code, a severity, a location, and a message.

  4. Report

    Print text or JSON, optionally write SARIF 2.1.0, and exit 0, 1, 2, or 3 for CI.