Skip to content

fix(lockfile): apply overrides before frozen-lockfile spec comparison#354

Merged
jdx merged 2 commits intomainfrom
fix/lockfile-overrides-frozen-check
Apr 27, 2026
Merged

fix(lockfile): apply overrides before frozen-lockfile spec comparison#354
jdx merged 2 commits intomainfrom
fix/lockfile-overrides-frozen-check

Conversation

@jdx
Copy link
Copy Markdown
Contributor

@jdx jdx commented Apr 27, 2026

Summary

When a name+range override (plist@<3.0.5>=3.0.5) fires on a direct dep, pnpm rewrites the lockfile's importer specifier: to the override target. aube's drift check only honored bare-name overrides via raw map lookup, so --frozen-lockfile reported stale on every install once the user added a version-keyed override.

Pull the matching logic into a new override_match module that parses name, name@<range>, @scope/name, and @scope/name@<range> keys and applies them against (name, manifest_spec). Parent-chain selectors (foo>bar, **/foo) are dropped at compile time — they can't fire on direct deps. Kept inside aube-lockfile to avoid a cross-crate dep cycle (aube-resolver already depends on aube-lockfile, so the existing matcher there isn't reachable from here).

Reported in #352 (item 7, frozen-lockfile-override-specifier).

Test plan

  • cargo clippy --all-targets -- -D warnings
  • cargo fmt --check
  • cargo test --package aube-lockfile — 214 tests pass, including new fresh_when_version_keyed_override_rewrites_importer_spec and 7 override_match unit tests
  • ./test/bats/bin/bats test/overrides.bats — all 25 tests pass, including new "frozen-lockfile accepts version-keyed override that rewrites importer specifier"

Note

Medium Risk
Changes drift detection logic used by --frozen-lockfile to account for override rewriting; incorrect matching could cause false Fresh/Stale results and affect whether installs re-resolve.

Overview
Fixes --frozen-lockfile drift checking to apply overrides before comparing manifest vs lockfile importer specifiers, including version-keyed override selectors (e.g. pkg@<x).

Introduces a small override_match module to parse and match direct-dependency override keys (bare and name@range, including scoped packages), ignore parent-chain selectors, and prefer more-specific (range-keyed) rules. Adds unit and Bats regression tests covering version-keyed overrides that rewrite importer specifiers.

Reviewed by Cursor Bugbot for commit 7e4444a. Bugbot is set up for automated code reviews on this repo. Configure here.

When a name+range override (`plist@<3.0.5` → `>=3.0.5`) fires on a
direct dep, pnpm rewrites the lockfile's importer specifier to the
override target. The previous drift check only honored bare-name
overrides via raw map lookup, so `--frozen-lockfile` reported stale
on every install once the user added a version-keyed override.

Pull the matching logic into a new `override_match` module that
parses `name`, `name@<range>`, `@scope/name`, and `@scope/name@<range>`
keys and applies them against (name, manifest spec). Parent-chain
selectors (`foo>bar`, `**/foo`) are dropped at compile time — they
can't fire on direct deps. Kept inside aube-lockfile to avoid a
cross-crate dep cycle (aube-resolver already depends on aube-lockfile).

Reported in #352.
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 27, 2026

Greptile Summary

Fixes a --frozen-lockfile false-positive where pnpm rewrites an importer's specifier after applying a version-keyed override (pkg@<range), causing aube to report stale on every subsequent frozen install. Introduces override_match with compile/apply to parse bare and name@range keys (including scoped packages) and apply them before the specifier comparison, replacing the old raw map lookup that only handled bare-name overrides.

Confidence Score: 5/5

Safe to merge — the bug fix is well-scoped, all three previously flagged issues are addressed, and new unit + integration tests lock in the correct behavior.

Only a P2 style issue remains (misleading comment in one test assertion). The core logic for sorting version-keyed rules before bare rules, the exclusive-> boundary bump, and the parent-chain-key filtering are all correct and directly tested.

No files require special attention.

Important Files Changed

Filename Overview
crates/aube-lockfile/src/override_match.rs New module implementing direct-dep override matching; covers bare names, version-keyed keys, scoped packages, and the exclusive-> boundary edge case. One misleading test comment (line 350) says ranges don't overlap when the assertion demonstrates they do.
crates/aube-lockfile/src/lib.rs Integrates override_match into the drift check: replaces a raw map lookup with compile/apply so both bare-name and version-keyed overrides are evaluated before the specifier comparison.
test/overrides.bats Adds an integration test verifying that --frozen-lockfile accepts a lockfile whose importer specifier was rewritten by a version-keyed override (is-number@<7.0.0 → 7.0.0).

Fix All in Claude Code

Reviews (2): Last reviewed commit: "fix(lockfile): handle gt-comparator, exc..." | Re-trigger Greptile

Comment thread crates/aube-lockfile/src/override_match.rs
Comment thread crates/aube-lockfile/src/override_match.rs
Comment thread crates/aube-lockfile/src/override_match.rs
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 08ae9e7. Configure here.

Comment thread crates/aube-lockfile/src/override_match.rs
…e in override matcher

Three bugs in the initial override_match landing, all caught in PR review:

1. Keys with `>` in the version req (`lodash@>=4.17.21`, `@scope/pkg@>1`)
   were silently dropped because parse_key rejected any key containing
   `>`. Port aube-resolver's split_segments walker, which distinguishes
   `>` as a comparator continuation from `>` as a chain separator.
2. Bare-name rules always shadowed version-keyed rules for the same
   package. BTreeMap iteration is alphabetical, so `"plist"` came
   before `"plist@<3"` in the compiled Vec and `apply`'s find_map fired
   the bare rule even when pnpm would apply the more specific one.
   Sort version-keyed rules first to mirror pnpm's "more specific
   selector wins".
3. range_could_satisfy(">3.0.5", "<3.0.5") returned `true` because
   lower_bound_version stripped the `>` greedily, yielding 3.0.5,
   which fails `<3.0.5` (boundary excluded) and falls through to the
   "probably matches" default. Bump the candidate above the boundary
   when the range is exclusive `>X.Y.Z`.

The existing first_matching_rule_wins test didn't catch (2) because
its probe (`"^3"`) failed the version-keyed rule's range regardless
of order. New tests pin all three behaviors.
@jdx jdx merged commit 9d50f3a into main Apr 27, 2026
17 checks passed
@jdx jdx deleted the fix/lockfile-overrides-frozen-check branch April 27, 2026 22:56
@greptile-apps greptile-apps Bot mentioned this pull request Apr 27, 2026
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.

1 participant