Skip to content

fix(integrity): dedupe packageIds when counting resolved packages (#506)#507

Closed
ggreif wants to merge 1 commit intocaffeinelabs:mainfrom
ggreif:gabor/fix-integrity-alias-count
Closed

fix(integrity): dedupe packageIds when counting resolved packages (#506)#507
ggreif wants to merge 1 commit intocaffeinelabs:mainfrom
ggreif:gabor/fix-integrity-alias-count

Conversation

@ggreif
Copy link
Copy Markdown
Contributor

@ggreif ggreif commented Apr 21, 2026

Summary

Fixes #506 — the "Mismatched number of resolved packages N vs M" failure on valid projects that use aliased dependencies.

Root cause

In cli/integrity.ts, the count comparison at the end of checkLockFile() uses:

Object.keys(lockFileJson.hashes).length !== packageIds.length
  • lockFileJson.hashes is keyed by packageId (name@version), naturally deduplicated by the object.
  • packageIds comes from resolvePackages() which retains the alias dependency-table keys (base, base@0, base@0.16) as separate entries. getPackageId() collapses them via getDepName(), but the resulting array keeps the duplicates.

So any project with aliased deps has a packageIds.length > hashes.size gap equal to the number of aliases, and the integrity check fails even when the lockfile is internally consistent.

Fix

One-liner: dedupe via new Set(packageIds).size before comparing. Also added a comment explaining the subtlety so the next person doesn't restore the naive comparison.

Repro

Covered in #506 (with attached mops.toml / mops.lock). Short form: any project with base = "0.16.0", x-client = "0.1.2", etc., where transitives introduce alias rows, fails mops install --lock update at the final step.

Test plan

🤖 Generated with Claude Code

@ggreif ggreif requested a review from a team as a code owner April 21, 2026 18:17
`getResolvedMopsPackageIds()` produces one entry per dependency-table
row, including alias rows like `base`, `base@0`, `base@0.16` all
resolving to the same `base@0.16.0`. The subsequent length comparison
against `Object.keys(lockFileJson.hashes).length` then fails — the
hashes object is naturally deduplicated by its packageId keys, so the
two counts diverge by the number of alias duplicates.

Compare unique packageId counts via `Set` to match the lock file's
semantics.

Fixes caffeinelabs#506

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ggreif ggreif force-pushed the gabor/fix-integrity-alias-count branch 2 times, most recently from 8f6b088 to 4be097c Compare April 21, 2026 18:56
@ggreif ggreif self-assigned this Apr 21, 2026
@ggreif ggreif added the bug Something isn't working label Apr 21, 2026
@ggreif
Copy link
Copy Markdown
Contributor Author

ggreif commented Apr 21, 2026

@Kamirus this is a candidate for v2.12.2

Kamirus added a commit that referenced this pull request Apr 22, 2026
Fixes #506. Alternative to #507.

## What changes for users

`mops install` (and any flow that runs `--lock check`) no longer fails
with `Mismatched number of resolved packages: N vs M` on projects whose
resolved deps include multiple aliases (e.g. `base`, `base@0`,
`base@0.16`) pinned to the same `name@version`. Previously the only
workaround was `--lock ignore`.

## Root cause

`getResolvedMopsPackageIds` returned a list with duplicates:
`resolvePackages()` keeps each alias as its own entry, then
`getPackageId` collapses the alias suffix via `getDepName`, so two
aliases pinning the same `name@version` produced the same id twice.
`mops.lock`'s `hashes` is keyed by packageId and naturally dedup'd, so
the count comparison in `checkLockFile` mismatched.

## Why this approach over #507

#507 dedupes only at the count comparison. This PR dedupes inside
`getResolvedMopsPackageIds`, which (a) makes the function's contract
match its name, and (b) stops sending duplicate ids to the canister's
`getFileHashesByPackageIds`. Downstream membership checks (`includes`,
`in`) were already dedupe-safe, so no other call site needed changes.

## Test plan

Added a regression fixture pinning `core` and `core@1` to `1.0.0` plus a
test asserting `mops install` exits clean and stderr doesn't contain the
mismatch error. Verified that reverting the fix makes the Jest test fail
with the expected error string.
@Kamirus
Copy link
Copy Markdown
Collaborator

Kamirus commented Apr 22, 2026

fixed in #509

@Kamirus Kamirus closed this Apr 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Integrity check miscounts alias-tuples as distinct packages ("Mismatched number of resolved packages N vs M")

2 participants