Skip to content

fix(linker): expose hidden hoist from global store#358

Merged
jdx merged 1 commit intomainfrom
fix/gvs-hidden-hoist
Apr 28, 2026
Merged

fix(linker): expose hidden hoist from global store#358
jdx merged 1 commit intomainfrom
fix/gvs-hidden-hoist

Conversation

@jdx
Copy link
Copy Markdown
Contributor

@jdx jdx commented Apr 28, 2026

Summary

  • Expose hidden-hoist links from the shared global virtual-store root so packages reached through canonical ~/.cache/aube/virtual-store/... paths can still resolve hoisted packages.
  • Keep project-local .aube/node_modules behavior unchanged for non-canonicalized paths.
  • Clean only dead or junk GVS hidden-hoist entries and reconcile current-project names, preserving live links written by other projects.
  • Add regression tests for GVS hidden-hoist resolution, shared cleanup behavior, hoist-disabled shared installs, and stale scope-shaped entries.

Root Cause

Some toolchains resolve package files through their real GVS path instead of the project-local .aube/<dep_path> symlink. From that real path, Node's upward node_modules walk cannot reach node_modules/.aube/node_modules, so packages that rely on hidden hoisting for undeclared imports fail. The reported @ngx-translate/core / rxjs failure is one instance of that shape.

Tradeoffs

Packages with undeclared imports should fix their manifests upstream. This PR does not add package-specific exceptions; it makes GVS preserve the same hidden-hoist compatibility behavior that aube already provides in the project-local virtual store.

Because the GVS hidden hoist is shared across projects, cleanup is intentionally conservative. Live links are preserved even when they are not in the current project graph; a current install only removes dead targets or junk entries before reconciling the names it owns.

Validation

  • cargo fmt --check
  • cargo test -p aube-linker test_global_virtual_store_hidden_hoist -- --nocapture
  • cargo test -p aube-linker
  • cargo clippy -p aube-linker --all-targets -- -D warnings
  • git diff --check

Note

Medium Risk
Changes filesystem linking/cleanup behavior for hidden-hoist entries in global-virtual-store mode; mistakes could break module resolution or delete shared links across projects.

Overview
Fixes hidden-hoist resolution in global virtual store (GVS) mode by mirroring the hidden-hoist symlinks into virtual_store/node_modules/ in addition to the existing project-local .aube/node_modules/, so canonicalized package paths can still resolve hoisted undeclared deps.

Refactors hidden-hoist linking into link_hidden_hoist_at and adds shared-safe cleanup via sweep_dead_hidden_hoist_entries, which only removes dangling links/non-link junk in the shared GVS directory (while project-local installs still fully wipe/rebuild). Adds regression tests covering GVS hidden-hoist creation, dead-entry pruning, and preservation of live shared links when hoist is disabled.

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

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 28, 2026

Greptile Summary

This PR fixes GVS installs where packages resolved via the canonical ~/.cache/aube/virtual-store/... path couldn't see the project-local hidden hoist, by mirroring hidden-hoist symlinks under virtual_store/node_modules/ in addition to the existing .aube/node_modules/ tree. The refactor introduces link_hidden_hoist_at, sweep_dead_hidden_hoist_entries, and friends to allow the shared GVS directory to remove only dead/dangling entries (rather than wiping the whole tree), preserving cross-project links. The core logic is sound and the existing reconcile_top_level_link correctly handles version-bump repointing.

Confidence Score: 5/5

Safe to merge; only P2 findings present, all relating to test coverage gaps rather than correctness defects in the new logic.

No new P0 or P1 issues found. The two P2 comments are about test coverage gaps (untested @scope/ live-link preservation path, and a cross-project hoist=false test that only exercises a single project). The implementation logic in sweep_dead_hidden_hoist_entries, sweep_dead_hidden_hoist_scope, and link_hidden_hoist_at is correct on inspection. The issues flagged in prior review rounds are known trade-offs documented in the PR and code comments.

No files require special attention beyond the test coverage gaps noted inline.

Important Files Changed

Filename Overview
crates/aube-linker/src/lib.rs Adds GVS hidden-hoist mirroring by refactoring into link_hidden_hoist_at, introduces sweep_dead_hidden_hoist_entries/sweep_dead_hidden_hoist_scope/sweep_dead_hidden_hoist_entry for shared-directory pruning, and adds three regression tests; cross-project link preservation logic is sound but two of the three new tests have coverage gaps.

Fix All in Claude Code

Reviews (5): Last reviewed commit: "fix(linker): mirror hidden hoist into gl..." | Re-trigger Greptile

@jdx jdx force-pushed the fix/gvs-hidden-hoist branch from e09c89c to 7b1c24f Compare April 28, 2026 13:12
Copy link
Copy Markdown
Contributor Author

jdx commented Apr 28, 2026

Addressed the shared-store race in 7b1c24f.

The project-local hidden hoist still does the wipe-and-repopulate because that directory is owned by one project. The shared GVS hidden hoist now runs additively: it preserves the existing virtual-store/node_modules/ directory, reconciles only the names present in the current graph, and skips fresh links. That avoids the concurrent-install window where one install could remove another install's shared hidden-hoist links.

Added test_global_virtual_store_hidden_hoist_is_additive to cover this path.

Validation:

  • cargo fmt --check
  • cargo test -p aube-linker test_global_virtual_store -- --nocapture
  • cargo test -p aube-linker
  • cargo clippy -p aube-linker --all-targets -- -D warnings

Comment thread crates/aube-linker/src/lib.rs
@jdx jdx force-pushed the fix/gvs-hidden-hoist branch from 7b1c24f to 700e81c Compare April 28, 2026 13:28
Copy link
Copy Markdown
Contributor Author

jdx commented Apr 28, 2026

Addressed the follow-up feedback in 700e81c.

Changes:

  • hoist=false now cleans the GVS hidden-hoist mirror instead of leaving old links active.
  • The shared GVS mirror now prunes stale/removed names one entry at a time, rather than deleting the whole virtual-store/node_modules/ directory.
  • Existing fresh links are still reconciled/skipped, so warm installs avoid unnecessary recreation.

Added coverage:

  • test_global_virtual_store_hidden_hoist_prunes_by_entry
  • test_global_virtual_store_hidden_hoist_cleans_when_disabled

Validation:

  • cargo fmt --check
  • cargo test -p aube-linker test_global_virtual_store_hidden_hoist -- --nocapture
  • cargo test -p aube-linker
  • cargo clippy -p aube-linker --all-targets -- -D warnings
  • git diff --check

This comment was generated by Codex.

Comment thread crates/aube-linker/src/lib.rs
@jdx jdx force-pushed the fix/gvs-hidden-hoist branch from 700e81c to 17ce225 Compare April 28, 2026 13:34
Copy link
Copy Markdown
Contributor Author

jdx commented Apr 28, 2026

Addressed the cross-project removal issue in 17ce225.

The GVS hidden-hoist cleanup no longer uses the current project's graph as the complete preserve set. Instead, the shared virtual-store/node_modules/ cleanup now only removes broken entries: dangling symlinks or non-link junk. Live symlinks whose targets still exist are preserved, even when they belong to another project's graph. Current-project names are still reconciled normally, so version bumps or target changes for the active install get corrected.

I also updated the PR body to call out the upstream angle: packages with undeclared imports should ideally fix their manifests, but aube still needs GVS to match its existing hidden-hoist compatibility behavior for the ecosystem that already relies on hoisting.

Validation:

  • cargo fmt --check
  • cargo test -p aube-linker test_global_virtual_store_hidden_hoist -- --nocapture
  • cargo test -p aube-linker
  • cargo clippy -p aube-linker --all-targets -- -D warnings
  • git diff --check

This comment was generated by Codex.

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 17ce225. Configure here.

Comment thread crates/aube-linker/src/lib.rs Outdated
@jdx jdx force-pushed the fix/gvs-hidden-hoist branch from 17ce225 to 9f4f334 Compare April 28, 2026 13:45
@jdx jdx changed the title fix(linker): mirror hidden hoist into global store fix(linker): expose hidden hoist from global store Apr 28, 2026
Copy link
Copy Markdown
Contributor Author

jdx commented Apr 28, 2026

Addressed the latest cleanup feedback in 9f4f334.

  • Scope-shaped entries in the shared GVS hidden hoist now verify that the @scope path is a real directory before treating it as a scope. Broken links, files, and other junk at that level go through the same dead-entry cleanup path.
  • Dead hidden-hoist links now use the linker's dir-or-file removal helper, which covers Windows junction cleanup instead of only trying remove_file.
  • Added a regression assertion for stale @scope-shaped entries.
  • Rewrote the PR title and description to call out the upstream manifest issue and the conservative shared-GVS cleanup behavior.

Validation run locally:

  • cargo fmt --check
  • cargo test -p aube-linker test_global_virtual_store_hidden_hoist -- --nocapture
  • cargo test -p aube-linker
  • cargo clippy -p aube-linker --all-targets -- -D warnings
  • git diff --check

This comment was generated by Codex.

@jdx jdx merged commit 44400c7 into main Apr 28, 2026
18 checks passed
@jdx jdx deleted the fix/gvs-hidden-hoist branch April 28, 2026 15:14
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