[GH-ISSUE #266] Fail fast when installed dependencies are stale against the lockfile #155

Closed
opened 2026-10-15 18:21:45 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @morluto on GitHub (Jul 16, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/266

Problem

Repository commands can fail with misleading compiler or runtime errors when an existing node_modules tree predates package.json or package-lock.json changes.

Observed in the current checkout:

  • node_modules was last updated on July 13;
  • @types/semver was added on July 14;
  • package-lock.json changed again on July 16;
  • the installed MCP client/server SDKs were 2.0.0-beta.3, while the repository requires 2.0.0-beta.4;
  • @types/semver@7.7.1 was declared in both package and lock files but absent locally;
  • npm ls --depth=0 reported missing, invalid-version, and extraneous dependencies.

npm run build then failed at Upgrade.ts with a missing semver declaration error. The declaration was not missing from the repository; the local installation was stale. This can misdirect maintainers and agents into changing correct source or dependency declarations.

Expected behavior

Developer verification commands should detect an out-of-sync dependency tree before compilation and print one exact remediation:

Installed dependencies do not match package-lock.json. Run `npm ci`, then retry.

The check must be non-mutating. build, test, and check must not automatically run npm ci or otherwise install dependencies.

Automatic refresh would be inappropriate because it can:

  • delete and recreate node_modules;
  • require network access;
  • execute dependency lifecycle scripts;
  • mutate a workspace during an otherwise local build;
  • make builds slower and less deterministic;
  • recurse through npm lifecycle hooks such as prepare.

Design direction

Add a lightweight repository-owned dependency-state preflight. Possible implementations include:

  1. compare a deterministic lockfile digest recorded after successful installation;
  2. compare root package requirements with installed package identities without network access;
  3. wrap an npm-provided local-tree check while normalizing its output to one concise remediation.

Avoid relying only on mtimes: branch switches, rebases, archives, and restored caches can preserve misleading timestamps. Avoid treating every optional transitive package as a root mismatch.

The check should distinguish:

  • missing required root dependency;
  • installed root version different from the lockfile;
  • stale lock/install identity;
  • harmless optional/transitive state that does not invalidate the repository environment.

Acceptance criteria

  • A missing required dependency is detected before TypeScript compilation.
  • An installed root dependency at the wrong version is detected before tests or runtime verification.
  • The failure names npm ci as the exact remediation.
  • The preflight does not access the network or mutate node_modules, package files, hooks, or generated output.
  • A dependency name or version mismatch is reported without dumping unrelated package-tree noise.
  • A clean npm ci installation passes the preflight.
  • Tests cover missing, wrong-version, stale-lock, aligned, optional, and extraneous-only cases.
  • CI still installs with npm ci; the preflight is not treated as a replacement for installation.
  • Contributor documentation explains that pulling lockfile changes requires rerunning npm ci.
  • Packaged consumer commands do not depend on repository-only lockfile state.

Non-goals

  • Automatically installing or upgrading dependencies from build/test commands.
  • Committing node_modules.
  • Replacing npm ci in CI or clean-checkout setup.
  • Rejecting harmless package-manager implementation details that do not alter required root dependencies.

Relationship

The stale local SDK means exact serialized MCP schema-size measurements in #264 should be refreshed after an aligned npm ci; the source-level contract findings remain independently valid.

Originally created by @morluto on GitHub (Jul 16, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/266 ## Problem Repository commands can fail with misleading compiler or runtime errors when an existing `node_modules` tree predates `package.json` or `package-lock.json` changes. Observed in the current checkout: - `node_modules` was last updated on July 13; - `@types/semver` was added on July 14; - `package-lock.json` changed again on July 16; - the installed MCP client/server SDKs were `2.0.0-beta.3`, while the repository requires `2.0.0-beta.4`; - `@types/semver@7.7.1` was declared in both package and lock files but absent locally; - `npm ls --depth=0` reported missing, invalid-version, and extraneous dependencies. `npm run build` then failed at `Upgrade.ts` with a missing `semver` declaration error. The declaration was not missing from the repository; the local installation was stale. This can misdirect maintainers and agents into changing correct source or dependency declarations. ## Expected behavior Developer verification commands should detect an out-of-sync dependency tree before compilation and print one exact remediation: ```text Installed dependencies do not match package-lock.json. Run `npm ci`, then retry. ``` The check must be non-mutating. `build`, `test`, and `check` must not automatically run `npm ci` or otherwise install dependencies. Automatic refresh would be inappropriate because it can: - delete and recreate `node_modules`; - require network access; - execute dependency lifecycle scripts; - mutate a workspace during an otherwise local build; - make builds slower and less deterministic; - recurse through npm lifecycle hooks such as `prepare`. ## Design direction Add a lightweight repository-owned dependency-state preflight. Possible implementations include: 1. compare a deterministic lockfile digest recorded after successful installation; 2. compare root package requirements with installed package identities without network access; 3. wrap an npm-provided local-tree check while normalizing its output to one concise remediation. Avoid relying only on mtimes: branch switches, rebases, archives, and restored caches can preserve misleading timestamps. Avoid treating every optional transitive package as a root mismatch. The check should distinguish: - missing required root dependency; - installed root version different from the lockfile; - stale lock/install identity; - harmless optional/transitive state that does not invalidate the repository environment. ## Acceptance criteria - [ ] A missing required dependency is detected before TypeScript compilation. - [ ] An installed root dependency at the wrong version is detected before tests or runtime verification. - [ ] The failure names `npm ci` as the exact remediation. - [ ] The preflight does not access the network or mutate `node_modules`, package files, hooks, or generated output. - [ ] A dependency name or version mismatch is reported without dumping unrelated package-tree noise. - [ ] A clean `npm ci` installation passes the preflight. - [ ] Tests cover missing, wrong-version, stale-lock, aligned, optional, and extraneous-only cases. - [ ] CI still installs with `npm ci`; the preflight is not treated as a replacement for installation. - [ ] Contributor documentation explains that pulling lockfile changes requires rerunning `npm ci`. - [ ] Packaged consumer commands do not depend on repository-only lockfile state. ## Non-goals - Automatically installing or upgrading dependencies from build/test commands. - Committing `node_modules`. - Replacing `npm ci` in CI or clean-checkout setup. - Rejecting harmless package-manager implementation details that do not alter required root dependencies. ## Relationship The stale local SDK means exact serialized MCP schema-size measurements in #264 should be refreshed after an aligned `npm ci`; the source-level contract findings remain independently valid.
bsakaguchi 2026-10-15 18:21:45 +00:00
Author
Owner

@morluto commented on GitHub (Jul 16, 2026):

Dependency-freshness slice implemented: network-free deps:check compares every direct production/dev package version in package-lock.json with node_modules/.package-lock.json, ignores unrelated extraneous/transitive state, and emits one npm ci remediation. It runs before build/check/pack/publish; tests cover aligned, wrong-version, missing direct dev dependency, and extraneous-only installs.

<!-- gh-comment-id:4990831658 --> @morluto commented on GitHub (Jul 16, 2026): Dependency-freshness slice implemented: network-free deps:check compares every direct production/dev package version in package-lock.json with node_modules/.package-lock.json, ignores unrelated extraneous/transitive state, and emits one npm ci remediation. It runs before build/check/pack/publish; tests cover aligned, wrong-version, missing direct dev dependency, and extraneous-only installs.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#155
No description provided.