Skip to content

chore: use global virtual store - #10587

Open
zkochan wants to merge 4 commits into
teambit:masterfrom
zkochan:enable-gvs
Open

chore: use global virtual store#10587
zkochan wants to merge 4 commits into
teambit:masterfrom
zkochan:enable-gvs

Conversation

@zkochan

@zkochan zkochan commented Aug 9, 2026

Copy link
Copy Markdown
Member

Proposed Changes

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Enable Bit global virtual store for pnpm installs

⚙️ Configuration changes 🕐 10-20 Minutes

Grey Divider

AI Description

• Enable Bit dependency resolver global virtual store for shared pnpm install layout.
• Regenerate pnpm lockfile to reflect the updated dependency installation strategy.
Diagram

graph TD
  cfg["workspace.jsonc"] --> resolver["Bit dependency resolver"] --> install["pnpm install"] --> lock["pnpm-lock.yaml"]
  install --> vstore["Virtual store links"] --> gvs[("Global virtual store")]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Keep per-workspace virtual store (default)
  • ➕ Maximum isolation between workspaces; fewer surprises if pnpm/Bit settings differ per repo
  • ➕ Less risk of cross-repo contamination if global store is corrupted
  • ➖ More disk usage and slower cold installs across multiple clones
  • ➖ Less effective caching on developer machines/CI runners
2. Configure pnpm virtual store globally via pnpm/.npmrc settings
  • ➕ Centralizes pnpm behavior across projects without Bit-specific configuration
  • ➕ Can be aligned with CI cache strategy in a single place
  • ➖ Doesn’t capture Bit-specific expectations; may diverge from Bit’s dependency resolver behavior
  • ➖ Harder to reason about when different tools manage install settings

Recommendation: Using Bit’s enableGlobalVirtualStore is a good fit when Bit owns installation/linking, because the behavior is explicit and scoped to the workspace config. Ensure CI caches and contributor docs (if any) assume the new store behavior, and watch for platform/path-related issues when developers share a global store across branches.

Files changed (2) +8889 / -13628

Other (2) +8889 / -13628
pnpm-lock.yamlRegenerate lockfile after enabling global virtual store +8888/-13628

Regenerate lockfile after enabling global virtual store

• Updates the pnpm lockfile to match the dependency installation/linking behavior when using a global virtual store. This is expected churn from changing pnpm/Bit install strategy and should be treated as an atomic lockfile refresh.

pnpm-lock.yaml

workspace.jsoncEnable global virtual store in Bit dependency resolver +1/-0

Enable global virtual store in Bit dependency resolver

• Adds 'enableGlobalVirtualStore: true' under 'teambit.dependencies/dependency-resolver' to make pnpm use a shared global virtual store for this workspace.

workspace.jsonc

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 9, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Prune deletes pnpm internals 🐞 Bug ☼ Reliability
Description
With enableGlobalVirtualStore enabled, pnpm keeps pnpm-owned dot entries under
node_modules/.pnpm, but pnpmPruneModules() does not exclude dot entries and will remove them
when they don’t appear in the lockfile’s package list. This can break or destabilize subsequent
installs by deleting pnpm-managed virtual-store state (or forcing it to be recreated unpredictably).
Code

workspace.jsonc[14]

+    "enableGlobalVirtualStore": true,
Evidence
The PR turns on the global virtual store, and the install flow always runs pnpm prune afterward. In
the global virtual store layout, repo tests/helpers document that node_modules/.pnpm contains
pnpm-owned dot entries; however, the prune implementation only excludes lock.yaml and
node_modules, so dot entries are eligible for deletion even though they are pnpm internals.

workspace.jsonc[13-16]
scopes/workspace/install/install.main.runtime.ts[516-527]
scopes/dependencies/pnpm/pnpm.package-manager.ts[447-449]
scopes/dependencies/pnpm/pnpm-prune-modules.ts[21-50]
e2e/harmony/global-virtual-store.e2e.ts[28-36]
components/legacy/e2e-helper/e2e-fs-helper.ts[62-77]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
After this PR enables `enableGlobalVirtualStore` in `workspace.jsonc`, installs run in the global virtual store layout where `node_modules/.pnpm` contains pnpm-owned entries (including dot-prefixed directories). The post-install prune step (`pnpmPruneModules`) currently treats *all* entries except `lock.yaml` and `node_modules` as prune candidates, so it may delete pnpm-owned dot entries.
### Issue Context
- The workspace runs a prune step after installs.
- Repo e2e/helper code explicitly treats dot entries under `node_modules/.pnpm` as pnpm-owned internals that should not be considered dependency directories.
### Fix Focus Areas
- scopes/dependencies/pnpm/pnpm-prune-modules.ts[21-50]
### Suggested fix
- Change `readPackageDirsFromVirtualStore()` to:
- use `readdir(..., { withFileTypes: true })`
- include **directories only**
- exclude entries that start with `.`
- keep excluding `node_modules` and `lock.yaml`
- Add/adjust an e2e or unit test for the global virtual store path to ensure prune does not remove dot-prefixed pnpm entries under `node_modules/.pnpm`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Schema missing GVS key 🐞 Bug ⚙ Maintainability
Description
workspace.jsonc now sets enableGlobalVirtualStore, but the repo’s workspace-jsonc-schema.json
does not define this property under teambit.dependencies/dependency-resolver, so schema-driven
validation/autocomplete cannot surface/validate the new config key.
Code

workspace.jsonc[14]

+    "enableGlobalVirtualStore": true,
Evidence
The PR adds enableGlobalVirtualStore to the dependency-resolver config, but the schema’s
dependency-resolver properties section enumerates many fields (e.g., nodeLinker,
packageImportMethod, etc.) and does not include enableGlobalVirtualStore anywhere in that
definition.

workspace.jsonc[13-16]
workspace-jsonc-schema.json[111-137]
workspace-jsonc-schema.json[251-308]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`workspace.jsonc` enables `enableGlobalVirtualStore`, but `workspace-jsonc-schema.json` does not declare this option in the `teambit.dependencies/dependency-resolver` schema. This creates schema/config drift: editors and any schema validation tooling won’t recognize the new key.
### Issue Context
The config key is a real, supported option in code (`DependencyResolverWorkspaceConfig.enableGlobalVirtualStore?: boolean`), so the schema should be updated to match.
### Fix Focus Areas
- workspace-jsonc-schema.json[51-320]
- workspace.jsonc[13-16]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Persisted links path mismatch ✓ Resolved 🐞 Bug ☼ Reliability
Description
The CircleCI workspace persists .pnpm-store/*/links, but Bit resolves the global virtual store at
/links; if the resolved storeDir is /home/circleci/bit/.pnpm-store (as configured), downstream
jobs that only attach the workspace may miss the actual links directory and end up with broken
node_modules symlinks.
Code

.circleci/config.yml[R685-688]

+            # only the global virtual store, not the content-addressable files/ it hardlinks from:
+            # the consumers of this workspace read node_modules, they never fetch packages, and
+            # carrying files/ too would duplicate every package in the archive.
+            - .pnpm-store/*/links
Evidence
CI sets pnpm store-dir to /home/circleci/bit/.pnpm-store and persists .pnpm-store/*/links,
while Bit’s pnpm package manager computes the global virtual store directory as /links; downstream
jobs like lint only attach the workspace and then run without reinstalling, so missing the real
links directory would make node_modules symlinks non-resolvable.

.circleci/config.yml[651-710]
scopes/dependencies/pnpm/pnpm.package-manager.ts[426-445]
.circleci/config.yml[11-20]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
CircleCI persists `.pnpm-store/*/links`, but Bit/pnpm’s global virtual store directory is computed as `<storeDir>/links`. If pnpm resolves `storeDir` to the configured `/home/circleci/bit/.pnpm-store`, the actual required directory would be `.pnpm-store/links`, which is not matched by `.pnpm-store/*/links`.
This can break downstream jobs that rely on the attached workspace (without reinstalling) because `node_modules` entries may symlink into the missing global virtual store.
## Issue Context
- CI explicitly sets `store-dir=/home/circleci/bit/.pnpm-store`.
- Bit’s pnpm adapter computes the global virtual store as `join(config.storeDir, 'links')`.
- Downstream jobs (e.g. `lint`) attach the workspace and run commands without reinstalling.
## Fix Focus Areas
- .circleci/config.yml[665-710]
## Suggested fix
Update `persist_to_workspace.paths` to persist the exact `links` directory that Bit/pnpm uses.
A pragmatic, layout-tolerant option that still avoids persisting `files/` is to include both possible layouts:
- `.pnpm-store/links`
- `.pnpm-store/*/links`
(If you want to be stricter/cleaner, ensure the persisted path exactly matches the resolved `<storeDir>/links` layout you expect in CI.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

4. Hard-coded storeDir path 🐞 Bug ⚙ Maintainability ⭐ New
Description
CircleCI hard-codes the pnpm store location to /home/circleci/bit/.pnpm-store, coupling the
install output to the current executor user/home + working_directory layout. If the executor
image/user or working_directory ever changes, the global virtual store can end up outside the
persisted workspace root and downstream jobs may get dangling node_modules symlinks.
Code

.circleci/config.yml[R678-679]

+            echo "storeDir: /home/circleci/bit/.pnpm-store" > pnpm-workspace.yaml &&
+            echo "pnpm-workspace.yaml" >> .git/info/exclude &&
Evidence
The CI config defines the job working directory via ~/bit, but the new store configuration and
verification step use a fixed /home/circleci/bit path, introducing coupling to the executor’s home
directory resolution.

.circleci/config.yml[15-20]
.circleci/config.yml[674-702]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The CircleCI setup writes `pnpm-workspace.yaml` with an absolute `storeDir` under `/home/circleci/bit`, which is brittle if the job’s working directory or executor home ever changes.

### Issue Context
- The job working directory is configured as `~/bit`.
- `storeDir` and its verification step repeat `/home/circleci/bit` explicitly.

### Fix Focus Areas
- .circleci/config.yml[15-20]
- .circleci/config.yml[674-702]

### Suggested fix
In the `bbit install` step, compute the store path from the actual runtime directory (e.g., the outer workspace root) and use that variable consistently for:
- the `storeDir:` written into `pnpm-workspace.yaml`
- the verification step

For example, set `STORE_DIR="$(cd .. && pwd)/.pnpm-store"` (or similar, depending on where you want the store relative to the persisted root) and write `storeDir: ${STORE_DIR}`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can keep summaries lean with Finding overflow, which tucks the rest behind 'View more'

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Previous review results

Review updated until commit 82d9421 ⚖️ Balanced

Results up to commit 23abbf3


🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)


Action required
1. Prune deletes pnpm internals 🐞 Bug ☼ Reliability
Description
With enableGlobalVirtualStore enabled, pnpm keeps pnpm-owned dot entries under
node_modules/.pnpm, but pnpmPruneModules() does not exclude dot entries and will remove them
when they don’t appear in the lockfile’s package list. This can break or destabilize subsequent
installs by deleting pnpm-managed virtual-store state (or forcing it to be recreated unpredictably).
Code

workspace.jsonc[14]

+    "enableGlobalVirtualStore": true,
Evidence
The PR turns on the global virtual store, and the install flow always runs pnpm prune afterward. In
the global virtual store layout, repo tests/helpers document that node_modules/.pnpm contains
pnpm-owned dot entries; however, the prune implementation only excludes lock.yaml and
node_modules, so dot entries are eligible for deletion even though they are pnpm internals.

workspace.jsonc[13-16]
scopes/workspace/install/install.main.runtime.ts[516-527]
scopes/dependencies/pnpm/pnpm.package-manager.ts[447-449]
scopes/dependencies/pnpm/pnpm-prune-modules.ts[21-50]
e2e/harmony/global-virtual-store.e2e.ts[28-36]
components/legacy/e2e-helper/e2e-fs-helper.ts[62-77]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
After this PR enables `enableGlobalVirtualStore` in `workspace.jsonc`, installs run in the global virtual store layout where `node_modules/.pnpm` contains pnpm-owned entries (including dot-prefixed directories). The post-install prune step (`pnpmPruneModules`) currently treats *all* entries except `lock.yaml` and `node_modules` as prune candidates, so it may delete pnpm-owned dot entries.
### Issue Context
- The workspace runs a prune step after installs.
- Repo e2e/helper code explicitly treats dot entries under `node_modules/.pnpm` as pnpm-owned internals that should not be considered dependency directories.
### Fix Focus Areas
- scopes/dependencies/pnpm/pnpm-prune-modules.ts[21-50]
### Suggested fix
- Change `readPackageDirsFromVirtualStore()` to:
- use `readdir(..., { withFileTypes: true })`
- include **directories only**
- exclude entries that start with `.`
- keep excluding `node_modules` and `lock.yaml`
- Add/adjust an e2e or unit test for the global virtual store path to ensure prune does not remove dot-prefixed pnpm entries under `node_modules/.pnpm`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
2. Schema missing GVS key 🐞 Bug ⚙ Maintainability
Description
workspace.jsonc now sets enableGlobalVirtualStore, but the repo’s workspace-jsonc-schema.json
does not define this property under teambit.dependencies/dependency-resolver, so schema-driven
validation/autocomplete cannot surface/validate the new config key.
Code

workspace.jsonc[14]

+    "enableGlobalVirtualStore": true,
Evidence
The PR adds enableGlobalVirtualStore to the dependency-resolver config, but the schema’s
dependency-resolver properties section enumerates many fields (e.g., nodeLinker,
packageImportMethod, etc.) and does not include enableGlobalVirtualStore anywhere in that
definition.

workspace.jsonc[13-16]
workspace-jsonc-schema.json[111-137]
workspace-jsonc-schema.json[251-308]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`workspace.jsonc` enables `enableGlobalVirtualStore`, but `workspace-jsonc-schema.json` does not declare this option in the `teambit.dependencies/dependency-resolver` schema. This creates schema/config drift: editors and any schema validation tooling won’t recognize the new key.
### Issue Context
The config key is a real, supported option in code (`DependencyResolverWorkspaceConfig.enableGlobalVirtualStore?: boolean`), so the schema should be updated to match.
### Fix Focus Areas
- workspace-jsonc-schema.json[51-320]
- workspace.jsonc[13-16]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Persisted links path mismatch ✓ Resolved 🐞 Bug ☼ Reliability
Description
The CircleCI workspace persists .pnpm-store/*/links, but Bit resolves the global virtual store at
/links; if the resolved storeDir is /home/circleci/bit/.pnpm-store (as configured), downstream
jobs that only attach the workspace may miss the actual links directory and end up with broken
node_modules symlinks.
Code

.circleci/config.yml[R685-688]

+            # only the global virtual store, not the content-addressable files/ it hardlinks from:
+            # the consumers of this workspace read node_modules, they never fetch packages, and
+            # carrying files/ too would duplicate every package in the archive.
+            - .pnpm-store/*/links
Evidence
CI sets pnpm store-dir to /home/circleci/bit/.pnpm-store and persists .pnpm-store/*/links,
while Bit’s pnpm package manager computes the global virtual store directory as /links; downstream
jobs like lint only attach the workspace and then run without reinstalling, so missing the real
links directory would make node_modules symlinks non-resolvable.

.circleci/config.yml[651-710]
scopes/dependencies/pnpm/pnpm.package-manager.ts[426-445]
.circleci/config.yml[11-20]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
CircleCI persists `.pnpm-store/*/links`, but Bit/pnpm’s global virtual store directory is computed as `<storeDir>/links`. If pnpm resolves `storeDir` to the configured `/home/circleci/bit/.pnpm-store`, the actual required directory would be `.pnpm-store/links`, which is not matched by `.pnpm-store/*/links`.
This can break downstream jobs that rely on the attached workspace (without reinstalling) because `node_modules` entries may symlink into the missing global virtual store.
## Issue Context
- CI explicitly sets `store-dir=/home/circleci/bit/.pnpm-store`.
- Bit’s pnpm adapter computes the global virtual store as `join(config.storeDir, 'links')`.
- Downstream jobs (e.g. `lint`) attach the workspace and run commands without reinstalling.
## Fix Focus Areas
- .circleci/config.yml[665-710]
## Suggested fix
Update `persist_to_workspace.paths` to persist the exact `links` directory that Bit/pnpm uses.
A pragmatic, layout-tolerant option that still avoids persisting `files/` is to include both possible layouts:
- `.pnpm-store/links`
- `.pnpm-store/*/links`
(If you want to be stricter/cleaner, ensure the persisted path exactly matches the resolved `<storeDir>/links` layout you expect in CI.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit ebfb668


🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)


Action required
1. Prune deletes pnpm internals 🐞 Bug ☼ Reliability
Description
With enableGlobalVirtualStore enabled, pnpm keeps pnpm-owned dot entries under
node_modules/.pnpm, but pnpmPruneModules() does not exclude dot entries and will remove them
when they don’t appear in the lockfile’s package list. This can break or destabilize subsequent
installs by deleting pnpm-managed virtual-store state (or forcing it to be recreated unpredictably).
Code

workspace.jsonc[14]

+    "enableGlobalVirtualStore": true,
Evidence
The PR turns on the global virtual store, and the install flow always runs pnpm prune afterward. In
the global virtual store layout, repo tests/helpers document that node_modules/.pnpm contains
pnpm-owned dot entries; however, the prune implementation only excludes lock.yaml and
node_modules, so dot entries are eligible for deletion even though they are pnpm internals.

workspace.jsonc[13-16]
scopes/workspace/install/install.main.runtime.ts[516-527]
scopes/dependencies/pnpm/pnpm.package-manager.ts[447-449]
scopes/dependencies/pnpm/pnpm-prune-modules.ts[21-50]
e2e/harmony/global-virtual-store.e2e.ts[28-36]
components/legacy/e2e-helper/e2e-fs-helper.ts[62-77]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
After this PR enables `enableGlobalVirtualStore` in `workspace.jsonc`, installs run in the global virtual store layout where `node_modules/.pnpm` contains pnpm-owned entries (including dot-prefixed directories). The post-install prune step (`pnpmPruneModules`) currently treats *all* entries except `lock.yaml` and `node_modules` as prune candidates, so it may delete pnpm-owned dot entries.
### Issue Context
- The workspace runs a prune step after installs.
- Repo e2e/helper code explicitly treats dot entries under `node_modules/.pnpm` as pnpm-owned internals that should not be considered dependency directories.
### Fix Focus Areas
- scopes/dependencies/pnpm/pnpm-prune-modules.ts[21-50]
### Suggested fix
- Change `readPackageDirsFromVirtualStore()` to:
- use `readdir(..., { withFileTypes: true })`
- include **directories only**
- exclude entries that start with `.`
- keep excluding `node_modules` and `lock.yaml`
- Add/adjust an e2e or unit test for the global virtual store path to ensure prune does not remove dot-prefixed pnpm entries under `node_modules/.pnpm`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
2. Schema missing GVS key 🐞 Bug ⚙ Maintainability ⭐ New
Description
workspace.jsonc now sets enableGlobalVirtualStore, but the repo’s workspace-jsonc-schema.json
does not define this property under teambit.dependencies/dependency-resolver, so schema-driven
validation/autocomplete cannot surface/validate the new config key.
Code

workspace.jsonc[14]

+    "enableGlobalVirtualStore": true,
Evidence
The PR adds enableGlobalVirtualStore to the dependency-resolver config, but the schema’s
dependency-resolver properties section enumerates many fields (e.g., nodeLinker,
packageImportMethod, etc.) and does not include enableGlobalVirtualStore anywhere in that
definition.

workspace.jsonc[13-16]
workspace-jsonc-schema.json[111-137]
workspace-jsonc-schema.json[251-308]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`workspace.jsonc` enables `enableGlobalVirtualStore`, but `workspace-jsonc-schema.json` does not declare this option in the `teambit.dependencies/dependency-resolver` schema. This creates schema/config drift: editors and any schema validation tooling won’t recognize the new key.

### Issue Context
The config key is a real, supported option in code (`DependencyResolverWorkspaceConfig.enableGlobalVirtualStore?: boolean`), so the schema should be updated to match.

### Fix Focus Areas
- workspace-jsonc-schema.json[51-320]
- workspace.jsonc[13-16]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Persisted links path mismatch ✓ Resolved 🐞 Bug ☼ Reliability
Description
The CircleCI workspace persists .pnpm-store/*/links, but Bit resolves the global virtual store at
/links; if the resolved storeDir is /home/circleci/bit/.pnpm-store (as configured), downstream
jobs that only attach the workspace may miss the actual links directory and end up with broken
node_modules symlinks.
Code

.circleci/config.yml[R685-688]

+            # only the global virtual store, not the content-addressable files/ it hardlinks from:
+            # the consumers of this workspace read node_modules, they never fetch packages, and
+            # carrying files/ too would duplicate every package in the archive.
+            - .pnpm-store/*/links
Evidence
CI sets pnpm store-dir to /home/circleci/bit/.pnpm-store and persists .pnpm-store/*/links,
while Bit’s pnpm package manager computes the global virtual store directory as /links; downstream
jobs like lint only attach the workspace and then run without reinstalling, so missing the real
links directory would make node_modules symlinks non-resolvable.

.circleci/config.yml[651-710]
scopes/dependencies/pnpm/pnpm.package-manager.ts[426-445]
.circleci/config.yml[11-20]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
CircleCI persists `.pnpm-store/*/links`, but Bit/pnpm’s global virtual store directory is computed as `<storeDir>/links`. If pnpm resolves `storeDir` to the configured `/home/circleci/bit/.pnpm-store`, the actual required directory would be `.pnpm-store/links`, which is not matched by `.pnpm-store/*/links`.
This can break downstream jobs that rely on the attached workspace (without reinstalling) because `node_modules` entries may symlink into the missing global virtual store.
## Issue Context
- CI explicitly sets `store-dir=/home/circleci/bit/.pnpm-store`.
- Bit’s pnpm adapter computes the global virtual store as `join(config.storeDir, 'links')`.
- Downstream jobs (e.g. `lint`) attach the workspace and run commands without reinstalling.
## Fix Focus Areas
- .circleci/config.yml[665-710]
## Suggested fix
Update `persist_to_workspace.paths` to persist the exact `links` directory that Bit/pnpm uses.
A pragmatic, layout-tolerant option that still avoids persisting `files/` is to include both possible layouts:
- `.pnpm-store/links`
- `.pnpm-store/*/links`
(If you want to be stricter/cleaner, ensure the persisted path exactly matches the resolved `<storeDir>/links` layout you expect in CI.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit 9424647


🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)


Action required
1. Prune deletes pnpm internals 🐞 Bug ☼ Reliability
Description
With enableGlobalVirtualStore enabled, pnpm keeps pnpm-owned dot entries under
node_modules/.pnpm, but pnpmPruneModules() does not exclude dot entries and will remove them
when they don’t appear in the lockfile’s package list. This can break or destabilize subsequent
installs by deleting pnpm-managed virtual-store state (or forcing it to be recreated unpredictably).
Code

workspace.jsonc[14]

+    "enableGlobalVirtualStore": true,
Evidence
The PR turns on the global virtual store, and the install flow always runs pnpm prune afterward. In
the global virtual store layout, repo tests/helpers document that node_modules/.pnpm contains
pnpm-owned dot entries; however, the prune implementation only excludes lock.yaml and
node_modules, so dot entries are eligible for deletion even though they are pnpm internals.

workspace.jsonc[13-16]
scopes/workspace/install/install.main.runtime.ts[516-527]
scopes/dependencies/pnpm/pnpm.package-manager.ts[447-449]
scopes/dependencies/pnpm/pnpm-prune-modules.ts[21-50]
e2e/harmony/global-virtual-store.e2e.ts[28-36]
components/legacy/e2e-helper/e2e-fs-helper.ts[62-77]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
After this PR enables `enableGlobalVirtualStore` in `workspace.jsonc`, installs run in the global virtual store layout where `node_modules/.pnpm` contains pnpm-owned entries (including dot-prefixed directories). The post-install prune step (`pnpmPruneModules`) currently treats *all* entries except `lock.yaml` and `node_modules` as prune candidates, so it may delete pnpm-owned dot entries.
### Issue Context
- The workspace runs a prune step after installs.
- Repo e2e/helper code explicitly treats dot entries under `node_modules/.pnpm` as pnpm-owned internals that should not be considered dependency directories.
### Fix Focus Areas
- scopes/dependencies/pnpm/pnpm-prune-modules.ts[21-50]
### Suggested fix
- Change `readPackageDirsFromVirtualStore()` to:
- use `readdir(..., { withFileTypes: true })`
- include **directories only**
- exclude entries that start with `.`
- keep excluding `node_modules` and `lock.yaml`
- Add/adjust an e2e or unit test for the global virtual store path to ensure prune does not remove dot-prefixed pnpm entries under `node_modules/.pnpm`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
2. Persisted links path mismatch 🐞 Bug ☼ Reliability ⭐ New
Description
The CircleCI workspace persists .pnpm-store/*/links, but Bit resolves the global virtual store at
<storeDir>/links; if the resolved storeDir is /home/circleci/bit/.pnpm-store (as configured),
downstream jobs that only attach the workspace may miss the actual links directory and end up with
broken node_modules symlinks.
Code

.circleci/config.yml[R685-688]

+            # only the global virtual store, not the content-addressable files/ it hardlinks from:
+            # the consumers of this workspace read node_modules, they never fetch packages, and
+            # carrying files/ too would duplicate every package in the archive.
+            - .pnpm-store/*/links
Evidence
CI sets pnpm store-dir to /home/circleci/bit/.pnpm-store and persists .pnpm-store/*/links,
while Bit’s pnpm package manager computes the global virtual store directory as <storeDir>/links;
downstream jobs like lint only attach the workspace and then run without reinstalling, so missing
the real links directory would make node_modules symlinks non-resolvable.

.circleci/config.yml[651-710]
scopes/dependencies/pnpm/pnpm.package-manager.ts[426-445]
.circleci/config.yml[11-20]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
CircleCI persists `.pnpm-store/*/links`, but Bit/pnpm’s global virtual store directory is computed as `<storeDir>/links`. If pnpm resolves `storeDir` to the configured `/home/circleci/bit/.pnpm-store`, the actual required directory would be `.pnpm-store/links`, which is not matched by `.pnpm-store/*/links`.

This can break downstream jobs that rely on the attached workspace (without reinstalling) because `node_modules` entries may symlink into the missing global virtual store.

## Issue Context
- CI explicitly sets `store-dir=/home/circleci/bit/.pnpm-store`.
- Bit’s pnpm adapter computes the global virtual store as `join(config.storeDir, 'links')`.
- Downstream jobs (e.g. `lint`) attach the workspace and run commands without reinstalling.

## Fix Focus Areas
- .circleci/config.yml[665-710]

## Suggested fix
Update `persist_to_workspace.paths` to persist the exact `links` directory that Bit/pnpm uses.

A pragmatic, layout-tolerant option that still avoids persisting `files/` is to include both possible layouts:
- `.pnpm-store/links`
- `.pnpm-store/*/links`

(If you want to be stricter/cleaner, ensure the persisted path exactly matches the resolved `<storeDir>/links` layout you expect in CI.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit c445ba1


🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)


Action required
1. Prune deletes pnpm internals 🐞 Bug ☼ Reliability
Description
With enableGlobalVirtualStore enabled, pnpm keeps pnpm-owned dot entries under
node_modules/.pnpm, but pnpmPruneModules() does not exclude dot entries and will remove them
when they don’t appear in the lockfile’s package list. This can break or destabilize subsequent
installs by deleting pnpm-managed virtual-store state (or forcing it to be recreated unpredictably).
Code

workspace.jsonc[14]

+    "enableGlobalVirtualStore": true,
Evidence
The PR turns on the global virtual store, and the install flow always runs pnpm prune afterward. In
the global virtual store layout, repo tests/helpers document that node_modules/.pnpm contains
pnpm-owned dot entries; however, the prune implementation only excludes lock.yaml and
node_modules, so dot entries are eligible for deletion even though they are pnpm internals.

workspace.jsonc[13-16]
scopes/workspace/install/install.main.runtime.ts[516-527]
scopes/dependencies/pnpm/pnpm.package-manager.ts[447-449]
scopes/dependencies/pnpm/pnpm-prune-modules.ts[21-50]
e2e/harmony/global-virtual-store.e2e.ts[28-36]
components/legacy/e2e-helper/e2e-fs-helper.ts[62-77]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
After this PR enables `enableGlobalVirtualStore` in `workspace.jsonc`, installs run in the global virtual store layout where `node_modules/.pnpm` contains pnpm-owned entries (including dot-prefixed directories). The post-install prune step (`pnpmPruneModules`) currently treats *all* entries except `lock.yaml` and `node_modules` as prune candidates, so it may delete pnpm-owned dot entries.

### Issue Context
- The workspace runs a prune step after installs.
- Repo e2e/helper code explicitly treats dot entries under `node_modules/.pnpm` as pnpm-owned internals that should not be considered dependency directories.

### Fix Focus Areas
- scopes/dependencies/pnpm/pnpm-prune-modules.ts[21-50]

### Suggested fix
- Change `readPackageDirsFromVirtualStore()` to:
 - use `readdir(..., { withFileTypes: true })`
 - include **directories only**
 - exclude entries that start with `.`
 - keep excluding `node_modules` and `lock.yaml`
- Add/adjust an e2e or unit test for the global virtual store path to ensure prune does not remove dot-prefixed pnpm entries under `node_modules/.pnpm`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

Comment thread workspace.jsonc
"resolveEnvsFromRoots": true
},
"teambit.dependencies/dependency-resolver": {
"enableGlobalVirtualStore": true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Prune deletes pnpm internals 🐞 Bug ☼ Reliability

With enableGlobalVirtualStore enabled, pnpm keeps pnpm-owned dot entries under
node_modules/.pnpm, but pnpmPruneModules() does not exclude dot entries and will remove them
when they don’t appear in the lockfile’s package list. This can break or destabilize subsequent
installs by deleting pnpm-managed virtual-store state (or forcing it to be recreated unpredictably).
Agent Prompt
### Issue description
After this PR enables `enableGlobalVirtualStore` in `workspace.jsonc`, installs run in the global virtual store layout where `node_modules/.pnpm` contains pnpm-owned entries (including dot-prefixed directories). The post-install prune step (`pnpmPruneModules`) currently treats *all* entries except `lock.yaml` and `node_modules` as prune candidates, so it may delete pnpm-owned dot entries.

### Issue Context
- The workspace runs a prune step after installs.
- Repo e2e/helper code explicitly treats dot entries under `node_modules/.pnpm` as pnpm-owned internals that should not be considered dependency directories.

### Fix Focus Areas
- scopes/dependencies/pnpm/pnpm-prune-modules.ts[21-50]

### Suggested fix
- Change `readPackageDirsFromVirtualStore()` to:
  - use `readdir(..., { withFileTypes: true })`
  - include **directories only**
  - exclude entries that start with `.`
  - keep excluding `node_modules` and `lock.yaml`
- Add/adjust an e2e or unit test for the global virtual store path to ensure prune does not remove dot-prefixed pnpm entries under `node_modules/.pnpm`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread .circleci/config.yml
Comment on lines +685 to +688
# only the global virtual store, not the content-addressable files/ it hardlinks from:
# the consumers of this workspace read node_modules, they never fetch packages, and
# carrying files/ too would duplicate every package in the archive.
- .pnpm-store/*/links

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Persisted links path mismatch 🐞 Bug ☼ Reliability

The CircleCI workspace persists .pnpm-store/*/links, but Bit resolves the global virtual store at
<storeDir>/links; if the resolved storeDir is /home/circleci/bit/.pnpm-store (as configured),
downstream jobs that only attach the workspace may miss the actual links directory and end up with
broken node_modules symlinks.
Agent Prompt
## Issue description
CircleCI persists `.pnpm-store/*/links`, but Bit/pnpm’s global virtual store directory is computed as `<storeDir>/links`. If pnpm resolves `storeDir` to the configured `/home/circleci/bit/.pnpm-store`, the actual required directory would be `.pnpm-store/links`, which is not matched by `.pnpm-store/*/links`.

This can break downstream jobs that rely on the attached workspace (without reinstalling) because `node_modules` entries may symlink into the missing global virtual store.

## Issue Context
- CI explicitly sets `store-dir=/home/circleci/bit/.pnpm-store`.
- Bit’s pnpm adapter computes the global virtual store as `join(config.storeDir, 'links')`.
- Downstream jobs (e.g. `lint`) attach the workspace and run commands without reinstalling.

## Fix Focus Areas
- .circleci/config.yml[665-710]

## Suggested fix
Update `persist_to_workspace.paths` to persist the exact `links` directory that Bit/pnpm uses.

A pragmatic, layout-tolerant option that still avoids persisting `files/` is to include both possible layouts:
- `.pnpm-store/links`
- `.pnpm-store/*/links`

(If you want to be stricter/cleaner, ensure the persisted path exactly matches the resolved `<storeDir>/links` layout you expect in CI.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 9424647

Comment thread workspace.jsonc
"resolveEnvsFromRoots": true
},
"teambit.dependencies/dependency-resolver": {
"enableGlobalVirtualStore": true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Schema missing gvs key 🐞 Bug ⚙ Maintainability

workspace.jsonc now sets enableGlobalVirtualStore, but the repo’s workspace-jsonc-schema.json
does not define this property under teambit.dependencies/dependency-resolver, so schema-driven
validation/autocomplete cannot surface/validate the new config key.
Agent Prompt
### Issue description
`workspace.jsonc` enables `enableGlobalVirtualStore`, but `workspace-jsonc-schema.json` does not declare this option in the `teambit.dependencies/dependency-resolver` schema. This creates schema/config drift: editors and any schema validation tooling won’t recognize the new key.

### Issue Context
The config key is a real, supported option in code (`DependencyResolverWorkspaceConfig.enableGlobalVirtualStore?: boolean`), so the schema should be updated to match.

### Fix Focus Areas
- workspace-jsonc-schema.json[51-320]
- workspace.jsonc[13-16]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit ebfb668

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 23abbf3

zkochan and others added 4 commits August 18, 2026 14:34
With enableGlobalVirtualStore, node_modules holds only symlinks into
<store-dir>/links. pnpm's default store is ~/.local/share/pnpm, outside
setup_harmony's persist_to_workspace root, so every job that merely
attaches the workspace received dangling symlinks (lint died on a
missing node_modules/oxlint/bin/oxlint).

Point store-dir under ~/bit and persist the links directory. files/ is
left out: the consumers of this workspace read node_modules, they never
fetch packages, and carrying the content-addressable store too would
duplicate every package in the archive.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
store-dir in .npmrc is ignored: pnpm keeps only npm-compatible settings
there, and bit installs through @pnpm/napi's config reader, which takes
storeDir from the pnpm-workspace.yaml cascade. The store stayed in
~/.local/share/pnpm, outside the persist_to_workspace root, so the
node_modules symlinks reaching into <storeDir>/links still dangled in
every job that only attaches the workspace.

Write a CI-only pnpm-workspace.yaml instead, and check the store landed
inside the workspace before persisting it — a persist path that matches
nothing is not an error, so the previous attempt failed as a
MODULE_NOT_FOUND in lint rather than in the job that got it wrong.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The global virtual store puts every package's real directory outside the
project, so a package that requires an undeclared dependency by bare name
no longer finds it: node resolves from the realpath, and the ancestor walk
out of <store>/links/@/mocha/... never reaches the project's node_modules
the way the walk out of node_modules/.pnpm/mocha@11.1.0/ did.

mocha requires the reporter, and mocha-multi-reporters requires each
reporter it composes, so both hops died — taking down every e2e job before
a single test ran. Both accept a path resolved against cwd, which is the
repo root for these scripts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread .circleci/config.yml
Comment on lines +678 to +679
echo "storeDir: /home/circleci/bit/.pnpm-store" > pnpm-workspace.yaml &&
echo "pnpm-workspace.yaml" >> .git/info/exclude &&

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Informational

1. Hard-coded storedir path 🐞 Bug ⚙ Maintainability

CircleCI hard-codes the pnpm store location to /home/circleci/bit/.pnpm-store, coupling the
install output to the current executor user/home + working_directory layout. If the executor
image/user or working_directory ever changes, the global virtual store can end up outside the
persisted workspace root and downstream jobs may get dangling node_modules symlinks.
Agent Prompt
### Issue description
The CircleCI setup writes `pnpm-workspace.yaml` with an absolute `storeDir` under `/home/circleci/bit`, which is brittle if the job’s working directory or executor home ever changes.

### Issue Context
- The job working directory is configured as `~/bit`.
- `storeDir` and its verification step repeat `/home/circleci/bit` explicitly.

### Fix Focus Areas
- .circleci/config.yml[15-20]
- .circleci/config.yml[674-702]

### Suggested fix
In the `bbit install` step, compute the store path from the actual runtime directory (e.g., the outer workspace root) and use that variable consistently for:
- the `storeDir:` written into `pnpm-workspace.yaml`
- the verification step

For example, set `STORE_DIR="$(cd .. && pwd)/.pnpm-store"` (or similar, depending on where you want the store relative to the persisted root) and write `storeDir: ${STORE_DIR}`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 82d9421

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