Skip to content

fix(install): map peer-suffixed source deps to their canonical store index - #885

Merged
jdx merged 6 commits into
aubepkg:mainfrom
rubnogueira:fix/invalid-canonical-dep
Jun 13, 2026
Merged

jdx merged 6 commits into
aubepkg:mainfrom
rubnogueira:fix/invalid-canonical-dep

Conversation

@rubnogueira

@rubnogueira rubnogueira commented Jun 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Fresh --force installs that pull a git or remote-tarball dependency which acquires a peer-context suffix (directly or via propagation from a peer-bearing descendant) could fail late in linking with ERR_AUBE_MISSING_PACKAGE_INDEX — e.g. some-pkg@git+<sha>(react@18.2.0) "is in the link graph but missing from package_indices".

  • Root cause: the streaming fetch keys source-dep indices under their canonical source coordinate (name@git+<short>), but the resolver's peer-context post-pass rewrites the graph dep_path to name@git+<short>(peer@ver). remap_indices_to_contextualized only matched the full contextualized dep_path or spec_key() (semver), so the source-coordinate key was never found and the index was dropped.

  • Fix: strip the peer-context suffix (parenthesized (peer@ver) tail or hashed _<10hex> marker) to recover the canonical coordinate and look the index up under it. This is the precise inverse of how the peer-context pass builds {canonical_base}{suffix}, so it reconstructs the exact streamed key. The post-filter_graph catch-up remains as a fallback, so coverage strictly improves.

Test plan

  • cargo test -p aube --lib remap / strip_peer_context — 4 new
    unit tests (parenthesized suffix, hashed suffix, suffixless, and
    the suffix-stripper helper)
  • cargo clippy -p aube — clean
  • Hermetic repro: a git+file:// dep that is an ancestor of a
    registry package peering on react, installed alongside react,
    installs cleanly into the global virtual store
  • Re-run the original failing aube install --force on the affected
    project with the rebuilt binary

Summary by CodeRabbit

  • Bug Fixes

    • Improved resolution of peer-related dependency keys, including correct canonical mapping for git-based packages and both (peer@ver) and (<short-hash>) peer suffix forms.
  • Documentation

    • Updated lockfile and settings docs to describe the new capped peer-id representation as a parenthesized short hash (<short-hash>).
  • Tests

    • Added/extended unit and regression coverage for peer-suffix normalization, capping behavior, and consistent remapping across canonical key derivations.

@coderabbitai

coderabbitai Bot commented Jun 13, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c3817828-150a-4e5d-aec8-e74ba4053a0c

📥 Commits

Reviewing files that changed from the base of the PR and between fb841d4 and dc93069.

📒 Files selected for processing (1)
  • crates/aube-resolver/src/tests.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/aube-resolver/src/tests.rs

📝 Walkthrough

Walkthrough

This PR standardizes parenthesized peer-context suffix handling, updates lockfile canonicalization to strip suffixes at the first (, adds a fallback canonical lookup during install remapping, and refreshes tests and documentation for the new parenthesized short-hash format.

Changes

Peer-suffix canonicalization and remapping

Layer / File(s) Summary
Peer-suffix helpers and propagation
crates/aube-resolver/src/peer_context.rs, crates/aube-resolver/src/tests.rs
Add is_hashed_peer_suffix, short_peer_hash, and effective_peer_suffix; remove old underscore-hash helpers; use effective_peer_suffix in propagation and update tests for parenthesized hashed suffixes.
Lockfile canonical key normalization
crates/aube-lockfile/src/npm/layout.rs, crates/aube-lockfile/src/npm/tests.rs
Introduce version_from_tail that truncates at first ( and use it in child_canonical_key, dep_value_as_version, and canonical_key_from_dep_path; update tests to cover (short-hash) capped suffixes.
Canonical index remapping with peer-suffix fallback
crates/aube/src/commands/install/fetch.rs
Add strip_peer_context_suffix and update remap_indices_to_contextualized to try a stripped canonical dep-path when contextualized lookup fails; include unit/regression tests for parenthesized, nested, capped-hash, scoped, and suffix-less cases.
Docs, settings, and small imports
crates/aube-resolver/src/builder.rs, crates/aube-resolver/src/lib.rs, crates/aube-settings/settings.toml, docs/settings/index.md
Update documentation strings and settings docs to describe the parenthesized short-hash (<short-hash>) cap format (first 32 chars of SHA-256) and adjust a test import to use is_hashed_peer_suffix.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • jdx/aube#857: Also updates peer-context canonicalization and dependency-path stripping for resolving stored package paths.

Poem

A rabbit nibbles tail and clause,
Trims parentheses without a pause,
(peer@ver) or (<hash>) now align,
Canonical hops in a cleaner line,
The burrow hums, suffixes shine. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately describes the main change: fixing the mapping of peer-suffixed source dependencies to their canonical store indices for install operations.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Jun 13, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes ERR_AUBE_MISSING_PACKAGE_INDEX on --force installs where a git/remote-tarball dependency acquires a peer-context suffix via the resolver's post-pass. Simultaneously, it replaces the old aube-specific _<10hex> hashed-suffix format with pnpm's canonical (<32hex>) parenthesized form (createPeerDepGraphHash), making lockfile keys byte-compatible with pnpm.

  • fetch.rs: Adds strip_peer_context_suffix (split at first () and inserts a canonical_dep_path lookup leg in remap_indices_to_contextualized so source-coordinate keys stored by the streaming fetch are found even after the peer-context pass appends a suffix.
  • peer_context.rs: Replaces hash_peer_suffix / strip_hashed_peer_suffix with effective_peer_suffix + is_hashed_peer_suffix; the capped format is now (<32hex>) instead of _<10hex>, with hash input being the suffix body (pnpm's dirName) rather than the full suffix string.
  • layout.rs: Drops strip_hashed_peer_suffix; all three lockfile key functions now strip only at ( — existing lockfiles written by prior aube versions with the old _<10hex> marker will map to wrong canonical keys (noted in a prior review comment).

Confidence Score: 4/5

The fetch-layer fix is precise and well-tested; the peer-context format change is internally consistent. The unresolved concern (already raised in a prior review) is that removing _<10hex> handling from the lockfile layout functions breaks parsing of any lockfile written by a previous aube version when the suffix cap was hit.

The core fetch.rs bug fix and peer_context.rs format migration are both correct. However, layout.rs no longer handles the old underscore-hex peer marker, so any project that previously triggered the suffix cap will have its existing lockfile misread after upgrading — canonical_key_from_dep_path, child_canonical_key, and dep_value_as_version silently return keys with the _ trailer still attached, which won't match any entry in the canonical map.

crates/aube-lockfile/src/npm/layout.rs — the three key-derivation functions no longer handle the old _<10hex> suffix format

Important Files Changed

Filename Overview
crates/aube/src/commands/install/fetch.rs Core fix: adds strip_peer_context_suffix and a second lookup leg in remap_indices_to_contextualized so git/tarball deps whose dep_path acquires a peer suffix still resolve their streamed canonical index; well-covered by 4 new unit tests
crates/aube-resolver/src/peer_context.rs Replaces hash_peer_suffix (_<10hex>) with effective_peer_suffix/short_peer_hash that produce the pnpm-compatible (<32hex>) parenthesized form; adds is_hashed_peer_suffix detector; propagation and visit paths updated consistently
crates/aube-lockfile/src/npm/layout.rs Removes strip_hashed_peer_suffix; canonical_key_from_dep_path/child_canonical_key/dep_value_as_version now only strip at '(' — existing lockfiles containing the old _<10hex> format (from previous aube versions) will silently map to wrong canonical keys (already flagged in a prior review comment)
crates/aube-lockfile/src/npm/tests.rs Tests updated to use new (<32hex>) hash format; old _hex assertions replaced with parenthesized-hash equivalents; coverage unchanged in intent
crates/aube-resolver/src/tests.rs hash_peer_suffix tests migrated to effective_peer_suffix/is_hashed_peer_suffix; new identity-within-cap test added; peer_suffix_is_hashed_when_exceeding_cap assertion updated to new format

Reviews (3): Last reviewed commit: "ci: fix lint" | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
crates/aube/src/commands/install/fetch.rs (1)

1105-1126: 💤 Low value

Consider consolidating duplicate strip_hashed_peer_suffix implementations.

This function is identical to strip_hashed_peer_suffix in crates/aube-lockfile/src/npm/layout.rs (line 238). If the peer-suffix format changes in the future, both implementations would need to stay in sync.

Consider exposing a shared utility (e.g., pub(crate) in aube-lockfile with a re-export, or a small shared crate) so the stripping logic lives in one place. Not blocking since both are currently correct and well-tested.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aube/src/commands/install/fetch.rs` around lines 1105 - 1126,
Duplicate function strip_hashed_peer_suffix should be consolidated into a single
shared utility and referenced from both places: move the implementation into a
common location (for example, add a pub(crate) function strip_hashed_peer_suffix
in the aube-lockfile crate or a small shared crate), export/re-export it so
callers can use the same symbol, then replace the duplicate local
implementations by calling that shared strip_hashed_peer_suffix function; ensure
visibility and imports are updated and run tests to confirm behavior is
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/aube/src/commands/install/fetch.rs`:
- Around line 1105-1126: Duplicate function strip_hashed_peer_suffix should be
consolidated into a single shared utility and referenced from both places: move
the implementation into a common location (for example, add a pub(crate)
function strip_hashed_peer_suffix in the aube-lockfile crate or a small shared
crate), export/re-export it so callers can use the same symbol, then replace the
duplicate local implementations by calling that shared strip_hashed_peer_suffix
function; ensure visibility and imports are updated and run tests to confirm
behavior is unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 241385b2-2d0d-482a-b451-8479ccaf6ef2

📥 Commits

Reviewing files that changed from the base of the PR and between 6e5a66a and da87998.

📒 Files selected for processing (1)
  • crates/aube/src/commands/install/fetch.rs

jdx commented Jun 13, 2026 •

Copy link
Copy Markdown
Collaborator

I cross-checked this against current pnpm source (pnpm/pnpm at 8dfe438). The core base-vs-contextualized mapping in this PR matches pnpm's model.

pnpm keeps the package's base identity separately from the peer-context dep path: resolveDependencies.ts builds pkgIdWithPatchHash from pkgResponse.body.id, then resolvePeers.ts appends createPeerDepGraphHash(...) when creating the contextualized graph dep path. Fetch/store indexing in packageRequester.ts uses the unsuffixed opts.pkg.id, while lockfile conversion maps peer snapshots back to base package records with removeSuffix(depPath).

So mapping source-backed contextualized dep paths like name@git+sha(peer@ver) back to the canonical streamed key name@git+sha is consistent with pnpm's behavior.

Important compatibility issue I noticed while checking this: current pnpm's long peer suffix hash is still parenthesized, i.e. (${shortHash}), because createPeerDepGraphHash always returns (${dirName}) after hashing. The bare _<10hex> shape appears to be aube's current resolver format, not current pnpm's lockfile dep-path format. If aube writes that shape into pnpm-lock.yaml, that is a pnpm lockfile compatibility bug and should be fixed rather than treated as an intentional divergence.

This comment was generated by Codex.

rubnogueira and others added 4 commits June 13, 2026 23:07
The peer-context pass capped over-long peer suffixes with a bare
`_<10hex>` marker, but pnpm's createPeerDepGraphHash always returns a
parenthesized `(<shortHash>)` — sha256 hex truncated to 32 chars, hashing
the suffix body (peers joined with `)(`, without the outer parens).
Writing the bare form into pnpm-lock.yaml is a lockfile compatibility
bug.

Emit pnpm's parenthesized form. Because the capped suffix is now a normal
parenthesized segment, the bare-marker strippers in install/fetch and
aube-lockfile are redundant and removed — the existing first-`(` split
recovers the canonical key — which also dissolves the duplicate-helper
nitpick.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jdx
jdx merged commit d336f1b into aubepkg:main Jun 13, 2026
17 checks passed
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.

2 participants