Skip to content

fix(install): close pnpm-lock.yaml parity and re-resolution gaps - #896

Merged
jdx merged 9 commits into
aubepkg:mainfrom
rubnogueira:fix/pnpmfile-and-lock-compat
Jun 16, 2026
Merged

jdx merged 9 commits into
aubepkg:mainfrom
rubnogueira:fix/pnpmfile-and-lock-compat

Conversation

@rubnogueira

@rubnogueira rubnogueira commented Jun 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes a cluster of pnpm-lock.yaml parity and re-resolution bugs surfaced while running aube install / aube upgrade in a sharedWorkspaceLockfile=false monorepo. After these changes aube's lockfile output and up-to-date checks match pnpm's, so a project that installs cleanly under pnpm no longer re-resolves on every install or writes a divergent
lockfile under aube.

What's fixed

Each item is a separate commit.

  1. Stop peer suffixes at the package that supplies the peer (fix(resolver)) — a transitively-needed optional peer (e.g. fdir's picomatch) leaked up the whole chain, so aube wrote xml2json@0.12.0(picomatch@4.0.4) where pnpm writes xml2json@0.12.0. The suffix is now suppressed once it reaches a package that provides the peer; the declarer keeps its suffix.

  2. Write hosted-git deps as codeload tarballs (fix(resolver)) — a github/gitlab/bitbucket dep pinned to a 40-char SHA is now recorded as a codeload RemoteTarball (name@https://codeload.github.com/<owner>/<repo>/tar.gz/<sha> with resolution: {gitHosted, integrity, tarball} + version:), matching pnpm, instead of the <url>.git#<sha> / type: git form. resolution: keys are sorted to match pnpm's emitter.

  3. Render git/tarball peer suffixes as resolved specs (fix(lockfile)) — completes ci(release-plz): grant contents:write to upload-assets caller #2 for peer suffixes: request-promise-core@1.1.4(request@https://codeload.github.com/<owner>/<repo>/tar.gz/<sha>) instead of aube's internal hashed (request@git+<hash>). Implemented as a symmetric translation (hashed→spec on write, spec→hashed on read) so a round-trip is a fixed point — a one-sided change would re-key every install and churn the lockfile.

  4. Stamp pnpm config checksums on update/remove/dedupe/audit (fix(install)) — only the main install path wrote packageExtensionsChecksum / pnpmfileChecksum; the other four commands resolved a fresh graph and dropped both fields. They now route through a shared finalize_lockfile_graph that stamps the checksums, honoring --ignore-pnpmfile / --pnpmfile.

  5. Re-resolve when the pnpmfile changes (fix(install)) — the warm-path settings hash never folded in the pnpmfile's own bytes, so editing a local .pnpmfile.{cjs,mjs} readPackage / afterAllResolved hook rode the warm path: the hook silently never re-applied (no readPackage output, stale node_modules + lockfile). The resolved pnpmfile's path + content is now hashed into the settings hash, mirroring pnpm's own up-to-date check. This also resolves the "lockfile doesn't regenerate when node_modules is out of sync" report.

Design note

The peer-suffix translation (#3) is intentionally symmetric and restricted to git / remote-tarball sources, so the writer's hashed→spec pass and the reader's spec→hashed pass are exact inverses. A divergence would re-key the graph on
read and churn the lockfile on the next install (the original re-resolve loop).

Test plan

  • cargo test -p aube-lockfile (337 + new) and -p aube-resolver green
  • New unit tests for the rewrite_peer_suffix walker (flat / nested / registry no-op / multi-segment / no-suffix)
  • New integration test: git/tarball peer suffix renders as the resolved spec on write and round-trips back to the hashed form on read (fixed point)
  • cargo clippy --all-targets -- -D warnings clean; cargo fmt --check clean
  • Verified end-to-end on a sharedWorkspaceLockfile=false monorepo: hosted-git deps + peer suffixes now match pnpm, a forced re-resolve round-trips with no churn, and .pnpmfile readPackage output appears under aube

Summary by CodeRabbit

  • New Features
    • Settings hashing now includes pnpmfile hook selection; lockfile graph finalization is reused consistently across flows.
    • GVS prewarm now skips materializing dep paths whose content affects virtual-store hashes.
    • Added WARN_AUBE_LOCKFILE_MALFORMED_PEER_SUFFIX to surface malformed pnpm dep_path peer-suffix tails verbatim.
  • Bug Fixes
    • Fixed pnpm dep_path peer-suffix parsing/rewriting (including nested peers) and ensured correct resolved-spec round-trips, preserving unbalanced parentheses verbatim.
    • Normalized hosted-git/remote tarball local handling and stabilized peer-context propagation (supplier stop; meta-only optionals stay bare).
  • Refactor
    • Standardized lockfile graph finalization logic across audit/dedupe/remove/update.
  • Tests
    • Added/updated regression and unit coverage for pnpm round-trips, peer behavior, graph-hash influence, and finalization/checksum scenarios.

rubnogueira and others added 4 commits June 14, 2026 22:53
propagate_peer_suffixes_to_ancestors lifted a descendant's
(peer@version) suffix onto every non-peer-declaring ancestor, even when
an ancestor supplied that peer itself. A transitively-needed optional
peer like fdir's picomatch then leaked all the way up the chain
(tinyglobby, node-gyp, node-expat, xml2json), so aube wrote
xml2json@0.12.0(picomatch@4.0.4) where pnpm writes xml2json@0.12.0.

Suppress a peer once it reaches a package that supplies it (in its own
dependencies or optionalDependencies): pnpm resolves the peer there and
stops the suffix, leaving the supplier and everything above it bare. The
peer *declarer* (fdir) keeps its suffix via the existing has_own_peers
gate.

Co-authored-by: Cursor <cursoragent@cursor.com>
pnpm records a github/gitlab/bitbucket dep pinned to a 40-char SHA as a
codeload `RemoteTarball` (`name@https://codeload…/tar.gz/<sha>` with
`resolution: {gitHosted, integrity, tarball}` + `version:`), not the
`<url>.git#<sha>` / `type: git` form aube emitted. The resolver already
fetched the codeload tarball; promote the resolved `LocalSource::Git` to
`RemoteTarball` whenever a flat HTTPS archive URL exists and there's no
`&path:` subpath, then emit `version:` for remote-tarball entries even
on fresh resolve (their dep_path is the hashed `url+<hash>` form, so the
`url_keyed` heuristic was false). Also sort `resolution:` keys
alphabetically to match pnpm's sorted-key emitter.

Co-authored-by: Cursor <cursoragent@cursor.com>
Only the main install path stamped packageExtensionsChecksum and
pnpmfileChecksum into the lockfile; update, remove, dedupe and
audit --fix resolved a fresh graph (checksums None) and wrote it
verbatim, dropping both fields from pnpm-lock.yaml. Add
install::finalize_lockfile_graph — detect lockfile kind, load the
resolve context + local pnpmfile, stamp checksums, then run the existing
prepare pass — and route the four commands through it before writing.
The shared-workspace merge path threads the same --ignore-pnpmfile /
--pnpmfile flags the member resolve used so the root stamp can't re-add
a pnpmfileChecksum the user opted out of.

Co-authored-by: Cursor <cursoragent@cursor.com>
The warm-path settings hash folded in the workspace YAML (so a
pnpmfilePath *setting* change busted it) but never the pnpmfile's own
bytes. Editing a local .pnpmfile.{cjs,mjs} readPackage/afterAllResolved
hook therefore rode the warm path: the hook silently never re-applied,
leaving node_modules and the lockfile stale and suppressing pnpm's
readPackage output. Hash the resolved pnpmfile's path + content into the
settings hash so a changed hook forces a full re-resolve, mirroring
pnpm folding the pnpmfile into its own up-to-date check.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jun 14, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 91b6e0ae-661a-4374-80ce-bc88663c096a

📥 Commits

Reviewing files that changed from the base of the PR and between ffbbb44 and f6a099d.

📒 Files selected for processing (4)
  • crates/aube-codes/src/warnings.rs
  • crates/aube-lockfile/src/pnpm/dep_path.rs
  • crates/aube-resolver/src/platform.rs
  • docs/error-codes.data.json
✅ Files skipped from review due to trivial changes (1)
  • docs/error-codes.data.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • crates/aube-codes/src/warnings.rs
  • crates/aube-resolver/src/platform.rs
  • crates/aube-lockfile/src/pnpm/dep_path.rs

📝 Walkthrough

Walkthrough

The PR introduces LocalSource::RemoteTarball for hosted-git packages without subpath selectors, adds rewrite_peer_suffix to translate between internal hashed dep_path peer-suffix forms and pnpm's spec form on write/read, fixes peer-suffix propagation to stop at the package that supplies a peer, unifies dependency-edge resolution across npm/pnpm/yarn/bun conventions, exposes a content_affected_dep_paths API to defer prewarm materialization, and centralizes lockfile graph finalization (with pnpmfile checksum stamping and warm-path hash busting) into a shared finalize_lockfile_graph helper consumed by all commands.

Changes

Hosted-git tarball representation and peer-suffix round-trip

Layer / File(s) Summary
Hosted-git → RemoteTarball LocalSource selection
crates/aube-resolver/src/local_source.rs
Adds hosted_git_local_source helper and wires it into both codeload-cache-hit and codeload-extract branches of resolve_git_source, emitting RemoteTarball (with git_hosted: true) when a codeload URL exists and no subpath selector is present.
dep_path peer-suffix rewriting logic
crates/aube-lockfile/src/pnpm/dep_path.rs
Adds outer_paren_segments, rewrite_peer_suffix (pub(super)), and rewrite_peer_reference to extract top-level parenthesized segments and rewrite flat peer heads through a caller-provided translate function while preserving outer heads and recursing into nested suffixes; includes comprehensive unit tests including unbalanced/truncated parenthesis preservation.
Unified dependency-edge resolution across lockfile conventions
crates/aube-lockfile/src/source.rs, crates/aube-resolver/src/platform.rs
Introduces resolve_dep_edge helper to unify how dependency edges (name, tail) are resolved to graph keys across npm/pnpm/yarn/bun storage conventions; wires it into optional-dep pruning, GC reachability, required-edge traversal, and peer-bubbling reverse relationships in platform.rs.
Lockfile writer: peer-suffix-to-spec rewriting
crates/aube-lockfile/src/pnpm/write.rs
Introduces peer_suffix_to_spec closure; applies it to registry dependency version fields, snapshot dependency values, and snapshot keys; broadens version: emission to include RemoteTarball; reorders WritableResolution fields to match pnpm v9 key ordering.
Lockfile reader: spec-to-hashed normalization pass
crates/aube-lockfile/src/pnpm/read.rs
Adds post-parse normalization step that detects local-source Git/RemoteTarball peers, builds spec-to-hash translator, then rekeys packages and updates dep_path on LockedPackage, dependencies, optional_dependencies, and importer DirectDep entries via rewrite_peer_suffix.
Peer-suffix propagation stops at supplier
crates/aube-resolver/src/peer_context.rs
Adds per-package provides map built from own dependencies and optional_dependencies; threads through collect DFS helper so peer names the package supplies are suppressed from its own decoration and upward propagation; skips rewriting for globally-shareable sources to prevent virtual-store splitting.
Peer-suffix propagation behavior tests
crates/aube-resolver/src/tests.rs
Adds peer_suffix_stops_at_supplier_of_the_peer validating suffix attaches only at declaring package, and meta_only_optional_peer_stays_bare_to_keep_singleton_shared asserting meta-only peers do not cause sibling suffix splitting.
Lockfile write/read round-trip regression tests and malformed-suffix handling
crates/aube-lockfile/src/pnpm/tests.rs, crates/aube-codes/src/warnings.rs, docs/error-codes.data.json
Updates existing tests to use canonical hashed keys; adds transitive_tarball_child_keyed_canonically_so_parent_symlink_resolves, fresh_resolved_codeload_tarball_writes_pnpm_version_and_resolution, and git_tarball_peer_suffix_renders_as_spec_and_round_trips validating write/parse round-trips; adds WARN_AUBE_LOCKFILE_MALFORMED_PEER_SUFFIX code for unbalanced parentheses preservation.

Content-affected dep-path detection for prewarm deferral

Layer / File(s) Summary
content_affected_dep_paths reverse-reachability API
crates/aube-lockfile/src/graph_hash.rs
Adds public content_affected_dep_paths function that performs reverse reachability from globally-shareable source leaves (git/remote tarball) to collect all ancestors, producing an order- and cycle-independent set; tests cover leafs, cycles, and non-shareable source cases.
GVS prewarm skips content-affected nodes
crates/aube/src/commands/install/materialize.rs
run_gvs_prewarm_materializer computes content_affected set and skips materialization for those dep_paths, deferring them to a later fetch phase while prewarm materializes content-independent entries.

finalize_lockfile_graph centralization and pnpmfile checksums

Layer / File(s) Summary
finalize_lockfile_graph helper, re-export, and async tests
crates/aube/src/commands/install/settings.rs, crates/aube/src/commands/install/mod.rs
Defines finalize_lockfile_graph (detects lockfile kind, loads workspace context, detects pnpmfile, stamps pnpm checksums, calls prepare_resolved_graph_for_lockfile_write); re-exports from mod.rs; includes async tests for checksum stamping with pnpm-lock, aube-lock, and conditional pnpmfile detection.
Commands wired to finalize_lockfile_graph
crates/aube/src/commands/audit.rs, crates/aube/src/commands/dedupe.rs, crates/aube/src/commands/remove.rs, crates/aube/src/commands/update.rs
audit, dedupe, remove replace prepare_resolved_graph_for_lockfile_write with awaited finalize_lockfile_graph; update additionally threads ignore_pnpmfile/cli_pnpmfile through merge helpers, adds absolute_cli_pnpmfile, and stamps root lockfile with workspace-root config while honoring member overrides.
Pnpmfile included in warm-path settings hash
crates/aube/src/state.rs
hash_settings switches to load_both to access pnpmfile_path, hashes the resolved pnpmfile path bytes and file contents; tests verify hash changes on pnpmfile additions, edits, and removals.

Sequence Diagram(s)

sequenceDiagram
    participant Cmd as audit/dedupe/<br/>remove/update
    participant Finalize as finalize_lockfile_graph
    participant Settings as install/settings
    participant ResolveCtx as ResolveCtx<br/>(workspace)
    participant Write as pnpm/write
    participant Read as pnpm/read

    Cmd->>Finalize: (cwd, &mut graph, manifest,<br/>ignore_pnpmfile, cli_pnpmfile)
    Finalize->>Settings: detect kind, load workspace,<br/>find pnpmfile
    Settings-->>Finalize: ResolveCtx + pnpmfile path
    Finalize->>Finalize: stamp package_extensions_checksum<br/>+ pnpmfile checksum
    Finalize->>Finalize: prepare_resolved_graph_for_lockfile_write
    Cmd->>Write: write lockfile
    Write->>Write: peer_suffix_to_spec:<br/>hashed → spec form
    Write-->>Cmd: written YAML<br/>(spec-form suffixes)
    Cmd->>Read: reparse lockfile
    Read->>Read: detect git/tarball locals,<br/>build spec→hash map
    Read->>Read: rewrite_peer_suffix:<br/>spec → hashed form
    Read-->>Cmd: normalized LockfileGraph<br/>(hashed keys)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~110 minutes

Possibly related PRs

  • jdx/aube#885: Both PRs focus on correctly handling pnpm-style peer-context suffixes enclosed in parentheses—main PR rewrites/preserves and canonicalizes those (peer@ver) tail segments during pnpm lockfile parse/write, while the retrieved PR strips those same peer-context suffix tails during install index remapping so keys resolve back to the canonical dep-path.
  • jdx/aube#855: Main PR's pnpm dep_path peer-suffix rewriting/normalization changes the exact (name@version)(...) dep-path string shapes that io.rs later validates via validate_resolution_shapes to reject mismatched resolution/local_source pairs.

Poem

🐇 A rabbit once lost in a lockfile maze,
Where hashed dep-paths tangled in cryptic ways.
"Rewrite the peer suffix!" the eager bunny cried,
With RemoteTarball, spec-forms, and fixes nationwide.
Now finalize_lockfile_graph knows each hook's score,
And warm paths bust fresh when you edit once more! 🔒✨

🚥 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 concisely summarizes the main objective of the PR: fixing pnpm-lock.yaml parity and re-resolution gaps for the install command.
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.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

A registry package that peers with a git or remote-tarball dep wrote the
suffix as aube's internal FS-safe hashed dep_path
(`request-promise-core@1.1.4(request@git+<hash>)`); pnpm writes the
resolved spec (`...(request@https://codeload.github.com/.../tar.gz/<sha>)`).
Translate hashed->spec on write and spec->hashed on read so the lockfile
matches pnpm and a round-trip stays a fixed point (a one-sided change would
re-key every install and churn the lockfile).

Co-authored-by: Cursor <cursoragent@cursor.com>
@rubnogueira
rubnogueira force-pushed the fix/pnpmfile-and-lock-compat branch from 9ff7fa2 to 13b9db7 Compare June 14, 2026 22:38

@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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/aube-lockfile/src/pnpm/write.rs (1)

86-92: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve peer suffixes when serializing contextualized git/tarball deps.

These local.specifier() fast paths drop the (...) suffix that apply_peer_contexts put on the dep path. For LocalSource::Git / LocalSource::RemoteTarball, that means the importer version, snapshot dependency value, and snapshot key all collapse to the bare URL, so distinct peer variants can overwrite each other and reparsing can’t reconstruct the original graph. The read side already strips URL-based peer suffixes during classification, so the writer needs to add them back here for parity.

Also applies to: 529-533, 555-558

🤖 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-lockfile/src/pnpm/write.rs` around lines 86 - 92, The
local.specifier() calls for Git and RemoteTarball sources are dropping the peer
context suffix (the `(...)` part) from the dep_path, which causes distinct peer
variants to collapse into the same URL and prevents proper reparsing of the
graph. You need to preserve and include the peer suffix when serializing these
contextualized dependencies. Check the dep.dep_path to see if it contains a peer
context suffix and append it back to the result of local.specifier() for
LocalSource::Git and LocalSource::RemoteTarball variants at the anchor location
(lines 86-92) and at the sibling locations mentioned in the consolidated_sites
(lines 529-533 and 555-558). Ensure the suffix is reconstructed consistently
across all three locations so the importer version, snapshot dependency value,
and snapshot key maintain the peer context information.
🤖 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.

Inline comments:
In `@crates/aube/src/commands/install/settings.rs`:
- Around line 554-574: The `finalize_lockfile_graph` function at line 564
silently ignores errors from `aube_manifest::workspace::load_both(cwd)` by using
`unwrap_or_default()`, which masks malformed pnpm-workspace.yaml files and
causes incorrect checksum stamping. Change the return type of
`finalize_lockfile_graph` from unit type to a Result type to propagate errors
properly. Replace the `unwrap_or_default()` call on `load_both()` with the `?`
operator to fail fast on invalid workspace configuration, matching the install
command's error-handling behavior. Update all callers of
`finalize_lockfile_graph` to handle the Result return type appropriately.

---

Outside diff comments:
In `@crates/aube-lockfile/src/pnpm/write.rs`:
- Around line 86-92: The local.specifier() calls for Git and RemoteTarball
sources are dropping the peer context suffix (the `(...)` part) from the
dep_path, which causes distinct peer variants to collapse into the same URL and
prevents proper reparsing of the graph. You need to preserve and include the
peer suffix when serializing these contextualized dependencies. Check the
dep.dep_path to see if it contains a peer context suffix and append it back to
the result of local.specifier() for LocalSource::Git and
LocalSource::RemoteTarball variants at the anchor location (lines 86-92) and at
the sibling locations mentioned in the consolidated_sites (lines 529-533 and
555-558). Ensure the suffix is reconstructed consistently across all three
locations so the importer version, snapshot dependency value, and snapshot key
maintain the peer context information.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 74f6e6c2-11b8-4f7a-bbbf-201db5ca9b23

📥 Commits

Reviewing files that changed from the base of the PR and between 41ef43e and 9ff7fa2.

📒 Files selected for processing (14)
  • crates/aube-lockfile/src/pnpm/dep_path.rs
  • crates/aube-lockfile/src/pnpm/read.rs
  • crates/aube-lockfile/src/pnpm/tests.rs
  • crates/aube-lockfile/src/pnpm/write.rs
  • crates/aube-resolver/src/local_source.rs
  • crates/aube-resolver/src/peer_context.rs
  • crates/aube-resolver/src/tests.rs
  • crates/aube/src/commands/audit.rs
  • crates/aube/src/commands/dedupe.rs
  • crates/aube/src/commands/install/mod.rs
  • crates/aube/src/commands/install/settings.rs
  • crates/aube/src/commands/remove.rs
  • crates/aube/src/commands/update.rs
  • crates/aube/src/state.rs

@greptile-apps

greptile-apps Bot commented Jun 14, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a cluster of pnpm-lock.yaml parity and re-resolution bugs in sharedWorkspaceLockfile=false monorepos, making aube's lockfile output and warm-path checks match pnpm's.

  • Peer-suffix fixes: stops the (peer@ver) suffix at the package that supplies the peer and skips git/tarball packages from receiving suffixes entirely; adds a symmetric hashed\u2194spec translation so peer suffixes in written lockfiles render as resolved URLs (pnpm parity) and round-trip back to the hashed in-memory form on re-read.
  • Hosted-git as codeload tarball: promotes github/gitlab/bitbucket deps pinned to a 40-char SHA from type: git to a RemoteTarball with resolution: {gitHosted, integrity, tarball}, matching pnpm's own format; keys are canonicalised on read to prevent dangling virtual-store symlinks.
  • Checksum stamping + pnpmfile hashing: finalize_lockfile_graph centralises packageExtensionsChecksum/pnpmfileChecksum stamping across update, remove, dedupe, and audit --fix; pnpmfile path + content are now folded into the warm-path settings hash so editing a .pnpmfile.cjs/mjs hook busts the warm path correctly.

Confidence Score: 5/5

Safe to merge — well-scoped bug fixes with extensive regression tests; the one non-blocking concern in hosted_git_local_source is a theoretical edge case both call sites already prevent in practice.

All five bug fixes are orthogonal, each backed by new unit and integration tests. The peer-suffix rewrite is symmetric (writer to spec, reader to hashed) so round-trips are fixed-points. The finalize_lockfile_graph refactor introduces one additional failure mode — a malformed pnpm-workspace.yaml now fails commands that previously silently continued — but this is intentional and documented. The only non-blocking concern is integrity.unwrap_or_default() in hosted_git_local_source, where both call sites already guarantee a non-None integrity when a codeload URL is present.

crates/aube-resolver/src/local_source.rs — the integrity.unwrap_or_default() in hosted_git_local_source warrants a follow-up to make the non-None invariant explicit.

Important Files Changed

Filename Overview
crates/aube-lockfile/src/graph_hash.rs Adds content_affected_dep_paths, a reverse-reachability BFS from globally-shareable source seeds; cycle-safe and covered by three new unit tests.
crates/aube-lockfile/src/pnpm/dep_path.rs Adds outer_paren_segments, rewrite_peer_suffix, rewrite_peer_reference; unbalanced-paren case emits WARN_AUBE_LOCKFILE_MALFORMED_PEER_SUFFIX and preserves the key verbatim.
crates/aube-lockfile/src/pnpm/read.rs Canonicalises git/remote-tarball package keys from raw URL form to name@url+hash on read and rewrites peer-suffix references the same way.
crates/aube-lockfile/src/pnpm/write.rs Adds peer_suffix_to_spec translation and reorders WritableResolution fields alphabetically to match pnpm sortKeys; adds version: emission for RemoteTarball entries.
crates/aube-resolver/src/local_source.rs Adds hosted_git_local_source to promote hosted-git deps to RemoteTarball; integrity.unwrap_or_default() silently produces empty-string integrity if integrity is None alongside a codeload URL.
crates/aube-resolver/src/peer_context.rs Adds provides map to suppress peer-suffix propagation at the supplier package and skips globally-shareable git/tarball packages from receiving peer suffixes.
crates/aube/src/commands/install/settings.rs Adds finalize_lockfile_graph that stamps pnpm config checksums and runs snapshot annotation passes with --ignore-pnpmfile/--pnpmfile support.
crates/aube/src/commands/update.rs Threads ignore_pnpmfile/cli_pnpmfile through all lockfile-write paths; adds absolute_cli_pnpmfile to anchor relative overrides before crossing into a different cwd.
crates/aube/src/state.rs Hashes pnpmfile path + content into the warm-path settings hash; switches from load_raw to load_both to obtain ws_config.pnpmfile_path for custom path detection.

Reviews (4): Last reviewed commit: "fix(lockfile): surface unbalanced peer-s..." | Re-trigger Greptile

finalize_lockfile_graph loaded pnpm-workspace.yaml with
unwrap_or_default(), so a malformed workspace file silently fell back to
an empty config and the stamped packageExtensionsChecksum /
pnpmfileChecksum were computed from the wrong inputs, then persisted —
desyncing config-drift detection. Propagate the parse error instead,
matching the install entry path. A missing or empty workspace file is
Ok(default), not an error, so single-package projects are unaffected.

Co-authored-by: Cursor <cursoragent@cursor.com>
@rubnogueira
rubnogueira marked this pull request as draft June 14, 2026 23:49
Content-pinned git / remote-tarball deps were being split into multiple
global-virtual-store entries, so a singleton package could load twice and
throw "Cannot find module" at runtime. Three gaps, all closed here:

- read: canonicalize a transitive git/tarball package's `name@<url>` head
  to the FS-safe `name@url+<hash>` form on parse (mirrors push_direct and a
  fresh resolve), so the parent's sibling symlink and the hasher's child
  lookup resolve instead of dangling.
- resolve: add `resolve_dep_edge` and walk git/tarball `name@url+<hash>`
  edges in filter_graph / mark_optional_packages /
  mark_transitive_peer_dependencies so those subtrees aren't GC'd or
  mis-marked; keep globally-shareable keys bare (pnpm never suffixes a
  non-registry depPath) and don't fold meta-only optional peers into the
  dep_path.
- prewarm: skip materializing content-affected nodes (a shareable source
  dep or any ancestor of one). Prewarm runs before fetch and hashes them
  content-less while the link phase hashes them content-ful; materializing
  them early stranded a duplicate content-less cohort with dangling source
  leaves.

Co-authored-by: Cursor <cursoragent@cursor.com>

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/aube-resolver/src/platform.rs (1)

271-277: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use resolve_dep_edge when pruning transitive optionals.

This pass still checks only tail / name@tail, so a platform-mismatched optional git/tarball child keyed as name@url+<hash> is not removed. The later GC now resolves that same edge correctly and keeps the unsupported optional package reachable.

Suggested fix
-            let child_key = if package_keys.contains(tail) {
-                tail.clone()
-            } else {
-                format!("{name}@{tail}")
-            };
-            let keep = !ignored.contains(name) && !mismatched_packages.contains(&child_key);
+            let child_is_mismatched =
+                match aube_lockfile::resolve_dep_edge(name, tail, |k| package_keys.contains(k)) {
+                    Some(child_key) => mismatched_packages.contains(&child_key),
+                    None => false,
+                };
+            let keep = !ignored.contains(name) && !child_is_mismatched;
🤖 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-resolver/src/platform.rs` around lines 271 - 277, The retain
closure filtering pkg.optional_dependencies is not correctly resolving
dependency edges for git/tarball optional dependencies with hashes (keyed as
name@url+<hash>). Replace the current inline logic that checks
package_keys.contains(tail) and constructs the child_key with a call to
resolve_dep_edge to properly resolve the dependency edge, which will handle all
dependency types correctly and ensure platform-mismatched optional packages are
properly removed.
🤖 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.

Outside diff comments:
In `@crates/aube-resolver/src/platform.rs`:
- Around line 271-277: The retain closure filtering pkg.optional_dependencies is
not correctly resolving dependency edges for git/tarball optional dependencies
with hashes (keyed as name@url+<hash>). Replace the current inline logic that
checks package_keys.contains(tail) and constructs the child_key with a call to
resolve_dep_edge to properly resolve the dependency edge, which will handle all
dependency types correctly and ensure platform-mismatched optional packages are
properly removed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 5bf6b2fd-5fa1-4143-8ff6-2742af9d548d

📥 Commits

Reviewing files that changed from the base of the PR and between 925143f and ffbbb44.

📒 Files selected for processing (9)
  • crates/aube-lockfile/src/graph_hash.rs
  • crates/aube-lockfile/src/lib.rs
  • crates/aube-lockfile/src/pnpm/read.rs
  • crates/aube-lockfile/src/pnpm/tests.rs
  • crates/aube-lockfile/src/source.rs
  • crates/aube-resolver/src/peer_context.rs
  • crates/aube-resolver/src/platform.rs
  • crates/aube-resolver/src/tests.rs
  • crates/aube/src/commands/install/materialize.rs
✅ Files skipped from review due to trivial changes (1)
  • crates/aube-lockfile/src/lib.rs

@rubnogueira
rubnogueira marked this pull request as ready for review June 16, 2026 00:52
rubnogueira and others added 2 commits June 16, 2026 02:00
…onals

The optional-dependency prune still matched only the `tail` / `name@tail`
conventions, so a platform-mismatched optional git/tarball child keyed as
`name@url+<hash>` survived the prune and was only dropped (if at all) by
the later GC. Route it through `resolve_dep_edge` like the other graph
walks so the mismatched optional is removed here too.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ing them

A pnpm dep_path peer suffix with an unclosed `(` (a truncated or
hand-corrupted lockfile entry) made the paren walker return no segments,
so the rewrite silently dropped everything after the `(` and shortened
the key — a quiet lookup miss / mis-resolve on the next install. Preserve
the bytes verbatim and emit WARN_AUBE_LOCKFILE_MALFORMED_PEER_SUFFIX so
the bad entry is diagnosable. Also cross-reference the resolver's twin
`outer_paren_segments` so the two copies don't diverge silently.

Co-authored-by: Cursor <cursoragent@cursor.com>
@rubnogueira

Copy link
Copy Markdown
Contributor Author

@CodeRabbit resume

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews resumed.

@jdx
jdx merged commit f867521 into aubepkg:main Jun 16, 2026
17 checks passed
@cursor cursor Bot mentioned this pull request Jun 16, 2026
@rubnogueira
rubnogueira deleted the fix/pnpmfile-and-lock-compat branch June 16, 2026 07:41
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