Skip to content

feat(envs): remove core envs from the manifest and their sources from the workspace - #10465

Draft
davidfirst wants to merge 286 commits into
masterfrom
remove-core-envs-from-manifest
Draft

feat(envs): remove core envs from the manifest and their sources from the workspace#10465
davidfirst wants to merge 286 commits into
masterfrom
remove-core-envs-from-manifest

Conversation

@davidfirst

@davidfirst davidfirst commented Jul 2, 2026

Copy link
Copy Markdown
Member

Removes the env aspects (teambit.react/react, teambit.harmony/node, teambit.harmony/aspect, teambit.envs/env, teambit.mdx/mdx, teambit.mdx/readme) from the core manifest to slim Bit. They now act like any other env, installed from the registry.

New default env: teambit.harmony/empty-env (core). A totally empty env - no compiler, no tester, no preview, no dependency policy. Components with no env configured use it and work fully offline out of the box (add → compile no-op → tag/snap → export). Since it has no behavior, it has nothing to drift when bit itself changes - the one env that is safe to keep core (and versionless in models) forever. To get a dev experience, users configure a real env (bit create flows already do).

teambit.harmony/aspect and teambit.envs/env are removed like the rest, with zero behavior change. Their implementation is untouched (react-based, preview and all) - users get the exact released behavior after bit install (the pinned-version machinery auto-installs them). New envs are created from the bitdev env packages (bit create react-env etc.), so these built-in envs are legacy surface. The bit-aspect template and the harmony starters moved to the core generator aspect, so bit create bit-aspect and bit new keep working out of the box (the created aspect needs bit install before it loads, like any env).

Versionless by design. Config entries for the removed env ids are persisted by name, without a version - exactly as they were when core (registered as core-extension names). Keeping them versionless is deliberate on two counts. First, it keeps the env from becoming a dependency edge of its own components; otherwise an env such as react, whose dependency closure includes components that use it as their env, creates circular TS project references and breaks lane/tag builds. Second, it preserves forward compatibility: a re-tag under the new bit keeps the env id versionless, so a teammate who has not upgraded yet (whose bit still ships these as core) can import the re-tagged component and resolve the env - instead of receiving a versioned id their bit has no component for. The alternative (showing the component as modified and pinning the env on the next tag) would silently break not-yet-upgraded consumers.

Backward compatibility. Old components have the removed envs saved without a version. legacy-core-envs.ts maps them to pinned versions, applied only at the resolution/loading/install level - stored objects are never mutated. Versionless legacy ids match the env slot ignoring version, bit install auto-adds their packages, and single-instance semantics are enforced (a loaded version is reused rather than loading another copy). Not-installed legacy envs fail fast with a NonLoadedEnv issue suggesting bit install - no scope-capsule isolation in workspace context (which used to take minutes). Old components load without being reported as modified, and re-tagging keeps the env versionless - covered end-to-end by e2e/harmony/legacy-core-env-back-compat.e2e.ts, which imports a component exported by a pre-removal bit (env saved versionless) and asserts it is not modified and stays versionless after a re-tag.

Relocated core wiring: the bit aspect CLI command moved to teambit.workspace/workspace; validateBeforePersistHook moved to teambit.dependencies/dependency-resolver; the dead @teambit/legacy link is now skipped instead of crashing.

Also fixes latent issues this path exposed: versionless seeders filtering out all manifests in loadExtensionsByManifests, circular env chains causing infinite component-load recursion, versioned core-aspect ids escaping core filters and doRequire mutating shared core manifests, stack overflows from recursive graph traversal, and a spurious MissingDists issue for compiler-less envs.

Verified locally: fresh workspace (JS and TS components) - clean status in ~1s, tag/snap/export offline, bit envs/bit test graceful; this repo's workspace - status/insights/list-core clean; the seven repo components that relied on the default env are now explicitly set to the node env. bit create <template> --env <removed-env> loads the env's templates on demand from the global scope (pinned version); this path also loads the full manifest graph, and binds manifest deps of legacy envs to their pinned versions (models built when these envs were core don't list them as dependencies). The e2e setCustomEnv helper installs the env package the fixture imports (e.g. @teambit/node).


Also removes the former-core env sources from this repo's workspace (scopes/harmony/node, scopes/react/react, scopes/harmony/aspect, scopes/envs/env, scopes/mdx/mdx, scopes/docs/readme) - bit now dogfoods them as installed packages like any consumer, and the source-vs-installed duality is gone. Making this pass end-to-end surfaced several general fixes that ride along:

  • workspace-aspects-loader: an unresolvable dependency-env no longer aborts the whole load group (it degrades to a reported load failure for that env only), and on aspect-path collisions the dedup keeps the def matching the requested id instead of the first one seen.
  • dependency-resolver: new fallback md/mdx import detector, so .docs.mdx imports are detected even when the mdx aspect isn't loaded (latent gap once mdx is no longer core - without it, docs deps silently drop from dependency computation and preview bundling fails).
  • builder: Module._extensions require hooks are restored after each build task. An in-process tester leaves @babel/register's pirates hook installed; the hook claims all .js files (including node_modules, regardless of babel ignore config) and breaks require() of ESM-only packages in every later task in the process (pirates drops the format arg node >=22.12 uses to route require(esm)).
  • preview: pre-bundle loads the mdx options via a native import() instead of a top-level require, immune to the same stale-hook hazard.
  • e2e: fixture env extensions marked @bit-no-check; timings manifest covers the split spec files so shard balancing accounts for the heavier env-install suites.

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

Copy link
Copy Markdown

PR Summary by Qodo

Load former core envs as regular registry envs with legacy version pinning

✨ Enhancement 🐞 Bug fix 🕐 40+ Minutes

Grey Divider

AI Description

• Remove env aspects from core manifest; load them as regular, versioned env components.
• Add legacy core-env mapping to pin versions and auto-install missing packages.
• Prevent recursion/stack overflows in aspect/env loading and graph traversal paths.
Diagram

graph TD
  A["Component env id (may be versionless)"] --> C["EnvsMain (env resolution)"] --> D["Aspects loaders (ws/scope)"] --> E["InstallMain (workspace policy)"] --> F["Registry packages (@teambit/*)"]
  C --> B["legacy-core-envs.ts (pinned versions)"] --> D
  C --> G["Fallback TS compiler"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Migrate stored component env ids to include versions
  • ➕ Eliminates ongoing special-casing for versionless ids
  • ➕ Makes resolution/slot lookups simpler and more consistent
  • ➖ Mutates historical objects/models (explicitly avoided by this PR)
  • ➖ Requires migration tooling and careful rollout across scopes/workspaces
2. Resolve legacy envs to a semver range (e.g. ^1.x) instead of pinned
  • ➕ Reduces maintenance of pinned versions
  • ➕ Allows automatic uptake of compatible env fixes
  • ➖ Less deterministic; can break builds when env behavior changes
  • ➖ Harder to reproduce old snapshots and debug regressions
3. Keep env aspects in core manifest but lazy-load/bundle-split
  • ➕ Avoids registry dependency for default/basic envs
  • ➕ Minimizes behavior change in resolution codepaths
  • ➖ Does not achieve the same binary/core slimming goal
  • ➖ Still couples env release cadence to core distribution

Recommendation: The PR’s approach (treat former core envs as regular external envs, while preserving backward compatibility via a non-mutating legacy-id resolver + pinned versions) is the best tradeoff for slimming the core without breaking old components. The main follow-up to ensure long-term health is to formalize the pinned-version bump as part of the release workflow (as noted in the PR description) and consider adding a small regression test matrix around versionless legacy env ids + fallback-default-env behavior.

Files changed (15) +503 / -74

Enhancement (7) +352 / -30
environments.main.runtime.tsAdd legacy core env compatibility and fallback default env +153/-25

Add legacy core env compatibility and fallback default env

• Introduces legacy-core-env detection, slot lookups that ignore version, and special handling for versionless legacy ids. Adds a minimal fallback default env (with TS transpiler) to keep commands working before env installation and prevents self-referential env component loading loops.

scopes/envs/envs/environments.main.runtime.ts

fallback-typescript-compiler.tsAdd minimal transpile-only TypeScript compiler for fallback env +43/-0

Add minimal transpile-only TypeScript compiler for fallback env

• Implements a lightweight TypeScript transpiler (no type-checking) used by the fallback default env to produce requirable dists in capsules when the real env is not installed/loaded yet.

scopes/envs/envs/fallback-typescript-compiler.ts

index.tsExport legacy core env utilities from envs public API +7/-0

Export legacy core env utilities from envs public API

• Re-exports helper functions for legacy core env identification, pinning, package naming, and id resolution so workspace/scope/install hosts can share the same compatibility logic.

scopes/envs/envs/index.ts

legacy-core-envs.tsDefine pinned versions and helpers for legacy core env ids +59/-0

Define pinned versions and helpers for legacy core env ids

• Adds a central mapping from legacy core env ids to pinned versions plus helpers to resolve versionless ids and derive registry package names. Includes a list of older removed env ids to suppress invalid-config errors even without a pinned package.

scopes/envs/envs/legacy-core-envs.ts

scope-aspects-loader.tsNormalize legacy core env ids to pinned versions in scope loading +10/-1

Normalize legacy core env ids to pinned versions in scope loading

• Resolves versionless legacy core env ids to pinned versions before importing/loading, enabling external env loading from registry. Improves core-aspect filtering to exclude core aspects even when requested with versions (dependency-induced).

scopes/scope/scope/scope-aspects-loader.ts

install.main.runtime.tsAuto-install legacy core env packages via workspace policy pinning +42/-1

Auto-install legacy core env packages via workspace policy pinning

• Adds legacy core envs used by components (without versions) to the workspace policy using pinned versions and derived @teambit/* package names. Extends missing-env package resolution to install pinned legacy env packages when env ids are versionless and not in workspace.

scopes/workspace/install/install.main.runtime.ts

workspace-component-loader.tsEnsure legacy core env extensions and default env participate in load groups +38/-3

Ensure legacy core env extensions and default env participate in load groups

• Collects name-only legacy env extensions so they are resolved and loaded before dependent components, and ensures DEFAULT_ENV is included for components without explicit env configuration. Treats legacy core env components as env aspects even when env-data is computed via fallback env.

scopes/workspace/workspace/workspace-component/workspace-component-loader.ts

Bug fix (5) +149 / -23
dev-files.main.runtime.tsSkip env manifest detection for legacy core env ids +3/-0

Skip env manifest detection for legacy core env ids

• Avoids fetching legacy core env components solely to look for env.jsonc, since old-style envs intentionally lack it. Keeps core/legacy envs out of dev-files env-manifest logic for faster/safer resolution.

scopes/component/dev-files/dev-files.main.runtime.ts

dependency-resolver.main.runtime.tsHarden env-root module resolution and legacy env policy handling +19/-4

Harden env-root module resolution and legacy env policy handling

• Guards getPackageDirInEnvRoot against cases where component env cannot be determined, falling back to root node_modules. Extends legacy peer-policy inclusion and env.jsonc detection to treat legacy core envs like core envs (no env.jsonc fetch).

scopes/dependencies/dependency-resolver/dependency-resolver.main.runtime.ts

aspect-loader.main.runtime.tsAvoid mutating shared core manifests when requiring aspects +7/-0

Avoid mutating shared core manifests when requiring aspects

• Prevents overriding manifest.id when require() resolves to a core aspect module, avoiding shared-object mutation that can break core aspect resolution (e.g. accidentally searching core ids with a version suffix).

scopes/harmony/aspect-loader/aspect-loader.main.runtime.ts

workspace-aspects-loader.tsLoad legacy envs reliably and guard against circular/aspect-graph recursion +93/-11

Load legacy envs reliably and guard against circular/aspect-graph recursion

• Adds versionless-legacy env matching when checking whether aspects are already loaded, resolves pinned versions for non-workspace legacy envs, and includes resolved ids as seeders to prevent manifest filtering bugs. Introduces in-flight load tracking to break circular env chains and replaces recursive predecessor traversal with safer inEdges-based logic to avoid stack overflows on large graphs.

scopes/workspace/workspace/workspace-aspects-loader.ts

workspace.tsTrack in-flight aspect loads and avoid recursive dependent traversal +27/-8

Track in-flight aspect loads and avoid recursive dependent traversal

• Adds a workspace-level inFlightAspectsLoads set used to prevent circular env/aspect load chains. Reworks getDependentsIds to iterative traversal to avoid maximum call stack errors, and skips misconfigured-env warnings for legacy core env ids.

scopes/workspace/workspace/workspace.ts

Refactor (1) +1 / -2
ui.main.runtime.tsDrop unused AspectMain dependency from UI deps tuple +1/-2

Drop unused AspectMain dependency from UI deps tuple

• Simplifies UI aspect dependency typing by removing an unused AspectMain type from UIDeps.

scopes/ui-foundation/ui/ui.main.runtime.ts

Tests (1) +1 / -7
core-aspects-ids.jsonUpdate core aspect id list to exclude former core envs +1/-7

Update core aspect id list to exclude former core envs

• Removes env aspect ids from the core-aspects test fixture list to reflect the slimmer core manifest set.

scopes/harmony/testing/load-aspect/core-aspects-ids.json

Other (1) +0 / -12
manifests.tsRemove env aspects from core manifests map +0/-12

Remove env aspects from core manifests map

• Stops bundling former core env aspects (node/react/mdx/readme/env/aspect-related) as core manifests, aligning with the new model where they are installed and loaded as regular env components.

scopes/harmony/bit/manifests.ts

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

qodo-free-for-open-source-projects Bot commented Jul 2, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (13) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Unfixable NonLoadedEnv remediation 🐞 Bug ≡ Correctness
Description
teambit.harmony/bit-custom-aspect is classified as a legacy-core env even though it has no pinned
package version, so versionless usage bypasses ExternalEnvWithoutVersion and becomes a
NonLoadedEnv whose hardcoded remediation is bit install (but install will never add it). This
can leave affected components blocked for tag/snap with a misleading/unsatisfiable fix instruction.
Code

scopes/envs/envs/environments.main.runtime.ts[R1356-1359]

+        // (except for envs that used to be core aspects - old components use them without a
+        // version, and bit knows how to install and load them)
+        if (!envIdStr.includes('@') && !isLegacyCoreEnvId(envIdStr)) {
const foundComp = components.find((c) => c.id.toStringWithoutVersion() === envIdStr);
Evidence
The repo explicitly marks teambit.harmony/bit-custom-aspect as an older removed core env with no
published package to pin, but isLegacyCoreEnv() includes it; env issue classification then treats
versionless legacy-core envs as valid and emits NonLoadedEnv instead of
ExternalEnvWithoutVersion. NonLoadedEnv’s remediation is always bit install, while the install
policy augmentation only happens when a pinned version exists—so this env can never be installed via
that mechanism.

scopes/envs/envs/legacy-core-envs.ts[22-55]
scopes/envs/envs/environments.main.runtime.ts[1348-1370]
components/component-issues/non-loaded-env.ts[3-7]
scopes/workspace/install/install.main.runtime.ts[935-955]

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

## Issue description
`OLDER_REMOVED_CORE_ENVS` entries (e.g. `teambit.harmony/bit-custom-aspect`) are treated as `isLegacyCoreEnvId() === true`, which causes `addNonLoadedEnvAsComponentIssues()` to emit `NonLoadedEnv` (solution hardcoded to `bit install`) instead of `ExternalEnvWithoutVersion`. But these env ids explicitly have no pinned version, and the install flow only auto-adds legacy-core env packages when a pinned version exists, so `bit install` cannot fix this issue.
### Issue Context
This breaks the intended UX/back-compat behavior for env ids that are allowed to remain in config for historical reasons but are not installable.
### Fix Focus Areas
- scopes/envs/envs/legacy-core-envs.ts[22-55]
- scopes/envs/envs/environments.main.runtime.ts[1348-1370]
- scopes/workspace/install/install.main.runtime.ts[935-955]
- components/component-issues/non-loaded-env.ts[3-7]
### Implementation guidance
- Introduce a helper that distinguishes **installable legacy core envs** from **older removed core envs**. For example:
- `isInstallableLegacyCoreEnv(id) := isLegacyCoreEnv(id) && Boolean(getPinnedLegacyCoreEnvVersion(id))`
- Keep `getLegacyCoreEnvsIds()` (for "persist-by-name") including older removed ids if needed.
- In `addNonLoadedEnvAsComponentIssues()`, only exempt versionless env ids from `ExternalEnvWithoutVersion` when `isInstallableLegacyCoreEnv(envIdStr)` is true.
- Optionally add a dedicated issue type/message for older-removed env ids (e.g. "env removed; must change env"), instead of producing `NonLoadedEnv` with `bit install`.
- Audit any other places using `envs.isLegacyCoreEnv()` for suppression/auto-remediation and ensure the older-removed ids don’t receive "install"-style treatment.

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


2. Stale installed-aspect cache 🐞 Bug ☼ Reliability
Description
getInstalledAspectResolver() now suppresses errors for non-requested dependency aspects
(throwOnError gated by requestedIds), but resolveInstalledAspectRecursively memoizes failures as
null and returns the cached null on later attempts. If an env/aspect package becomes available
later in the same process (e.g. during multi-cycle bit install), the loader won’t retry resolution
and the aspect can remain unresolved until cache invalidation/restart.
Code

scopes/workspace/workspace/workspace-aspects-loader.ts[R834-836]

+      const localPath = await this.resolveInstalledAspectRecursively(component, rootIds, graph, {
+        throwOnError: opts.throwOnError && isRequested,
+      });
Evidence
The new requestedIds gating makes resolution failures for dependency aspects non-fatal, allowing
them to flow into the negative-cache (null) write; later attempts short-circuit on the cache and
do not retry. Workspace cache clearing does not clear this map, so the stale negative result can
persist within the same process even after packages become available.

scopes/workspace/workspace/workspace-aspects-loader.ts[823-868]
scopes/workspace/workspace/workspace-aspects-loader.ts[925-933]
scopes/workspace/workspace/workspace.ts[871-890]

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

## Issue description
`WorkspaceAspectsLoader.resolveInstalledAspectRecursively()` caches failed resolutions as `null` in `resolvedInstalledAspects`. This PR also introduces a path where dependency aspects are resolved with `throwOnError: false` (based on `requestedIds`), so transient resolution failures during install can be cached and then never retried after packages are installed in the same process.
## Issue Context
- The installed-aspect resolver memoizes both successes and failures.
- Workspace cache clearing (`workspace.clearCache`) does not clear `resolvedInstalledAspects`.
- During `bit install` (and other multi-stage flows), aspects may become resolvable after `node_modules` changes, but the loader will still return cached `null`.
## Fix Focus Areas
- Add an explicit invalidation method on `WorkspaceAspectsLoader` (e.g. `clearResolvedInstalledAspectsCache()`), and call it from `Workspace.clearCache()` (and/or other places that mutate/refresh node_modules, such as post-install hooks).
- Alternatively, avoid caching `null` (or cache it only for the duration of a single load trace), so subsequent attempts can re-resolve after installation.
### Code references
- scopes/workspace/workspace/workspace-aspects-loader.ts[823-937]
- scopes/workspace/workspace/workspace.ts[871-890]

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


3. Empty-env main points dist 🐞 Bug ≡ Correctness
Description
NodeModuleLinker.createPackageJson rewrites TS/TSX mains to dist/*.js unless it can positively
detect teambit.harmony/empty-env, but linkToNodeModulesByIds loads components with
loadExtensions: false so empty-env components with no explicit env config can be misdetected and
get a non-existent dist main. This breaks requiring/importing such components from node_modules
because only source files are linked and empty-env has no compiler to generate dists.
Code

scopes/workspace/modules/node-modules-linker/node-modules-linker.ts[R280-283]

+    if (
+      !isCompilerLessEnv &&
+      typeof mainFile === 'string' &&
+      /\.(ts|tsx|jsx|mts|cts)$/.test(mainFile) &&
Evidence
The linking path explicitly loads components with extensions disabled, then calls createPackageJson,
which uses envs extension data/config to decide whether to rewrite main. For default empty-env
components without explicit env config, both values can be absent, so isCompilerLessEnv becomes
false and main is rewritten to dist/..., but the linker only symlinks bitmap/source files and
empty-env is defined to provide no compiler/dists.

scopes/workspace/modules/node-modules-linker/node-modules-linker.ts[368-383]
scopes/workspace/modules/node-modules-linker/node-modules-linker.ts[49-101]
scopes/workspace/modules/node-modules-linker/node-modules-linker.ts[160-167]
scopes/workspace/modules/node-modules-linker/node-modules-linker.ts[169-177]
scopes/envs/envs/environments.main.runtime.ts[112-115]
scopes/harmony/empty-env/empty-env.main.runtime.ts[8-16]

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

## Issue description
`NodeModuleLinker.createPackageJson()` rewrites a TS/TSX/etc `main` field to `dist/<main>.js` unless `isCompilerLessEnv` is true. In the `linkToNodeModulesByIds()` flow, components are loaded with `loadExtensions: false`, so `envsExt.data.id` is typically unset; for components that rely on the new default env (empty-env) and have no explicit env config, `configuredEnvId` is also unset, making `isCompilerLessEnv` false and forcing a dist main that will never exist under empty-env.
### Issue Context
This breaks consumers that import these linked packages via node resolution because the linker symlinks only bitmap/source files (not compiled `dist`), and empty-env intentionally has no compiler/dists.
### How to fix
1. Treat “no env configured / env data missing” as empty-env in this linking path (since `DEFAULT_ENV` is now `teambit.harmony/empty-env`).
2. Keep the existing safety behavior when empty-env is only a fallback for a *configured* non-empty env (i.e., if `configuredEnvId` exists and is not empty-env, do **not** treat it as compiler-less).
A concrete approach:
- Compute an `effectiveConfiguredEnvId = envsExt?.config?.env?.split('@')[0] ?? 'teambit.harmony/empty-env'`.
- Compute `effectiveDataEnvId = envsExt?.data?.id?.split('@')[0]`.
- Set `isCompilerLessEnv = (effectiveConfiguredEnvId === 'teambit.harmony/empty-env') && ((effectiveDataEnvId ?? effectiveConfiguredEnvId) === 'teambit.harmony/empty-env')`.
- Only rewrite `main` when `!isCompilerLessEnv`.
### Fix Focus Areas
- scopes/workspace/modules/node-modules-linker/node-modules-linker.ts[263-287]
- scopes/workspace/modules/node-modules-linker/node-modules-linker.ts[368-383]

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


View action required (1)
4. Versioned env lookup fails ✓ Resolved 🐞 Bug ≡ Correctness
Description
EnvsMain.getEnvDefinitionById() can no longer resolve a versioned env ID to an env registered in the
slot under its versionless ID, because getEnvDefinitionByStringId() only performs legacy-core
fallbacks for versionless IDs. This can cause env resolution to fail (and fall back to default env /
warnings) in flows where aspect-entry IDs become versioned (e.g. during tag) while the env slot
entry remains versionless.
Code

scopes/envs/envs/environments.main.runtime.ts[R1209-1212]

+    if (!envId.includes('@')) {
+      // versionless references hit the slot only for legacy core envs, which old components store
+      // without a version by design while the loaded env registers versioned. any other env must
+      // be looked up with its exact version: two components in the same workspace may use the
Evidence
calculateEnv() explicitly relies on getEnvDefinitionById(matchedEntry.id) because aspect-entry
IDs can change versions during tag and not match the env slot registration; with the new
getEnvDefinitionByStringId() behavior, that lookup can no longer succeed when the slot key is
versionless. Additionally, isEnvRegistered() documents/implements that versioned IDs should match
a versionless slot entry, but getEnvDefinitionByStringId() does not provide the analogous fallback
for env definition retrieval.

scopes/envs/envs/environments.main.runtime.ts[847-864]
scopes/envs/envs/environments.main.runtime.ts[1197-1219]
scopes/envs/envs/environments.main.runtime.ts[1247-1253]

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

## Issue description
`EnvsMain.getEnvDefinitionById()` calls `getEnvDefinitionByStringId(id.toString())` and then `getEnvDefinitionByStringId(id.toString({ ignoreVersion: true }))`. After this PR, `getEnvDefinitionByStringId()` only performs a special lookup for *versionless* IDs (and only for legacy core envs). This means a **versioned** ID (e.g. `my-scope/my-env@1.0.0`) will not match an env that is registered in the slot under `my-scope/my-env`.
This breaks env resolution in scenarios explicitly documented in `calculateEnv()` where aspect-entry IDs can become versioned during tag even though the slot registration isn’t.
### Issue Context
The code already acknowledges that versioned IDs should match versionless slot entries (see `isEnvRegistered()`), but `getEnvDefinitionById()` / `getEnvDefinitionByStringId()` do not implement the same matching behavior.
### Fix Focus Areas
- scopes/envs/envs/environments.main.runtime.ts[1197-1220]
### Suggested fix
Implement a safe fallback for **versioned -> versionless** lookup when the exact lookup misses:
- In `getEnvDefinitionById()` (preferred):
- After failing exact match, try `id.toStringWithoutVersion()` **only if** `this.envSlot.get(id.toStringWithoutVersion())` exists, and return that `EnvDefinition`.
- Or in `getEnvDefinitionByStringId()`:
- If `envId.includes('@')` and `this.envSlot.get(envId.split('@')[0])` exists, return that.
This preserves the PR’s intent of avoiding ambiguous ignore-version scans across multiple versions, while still supporting the explicit versionless-slot contract used by core/workspace envs.

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



Remediation recommended

5. Legacy env canonical ID unstable ✓ Resolved 🐞 Bug ☼ Reliability
Description
AspectLoaderMain.getLoadedAspectIdIgnoringVersion() returns the first loaded aspect ID matching the
versionless ID, making legacy-core env canonicalization dependent on Harmony extension ordering
rather than a deterministic policy (e.g. highest version).
DependencyResolverMain.getCanonicalLegacyCoreEnvId() uses this result to enforce legacy-core env
single-instance semantics, so when multiple versions are loaded it can bind to an unintended version
despite EnvsMain explicitly sorting/warning for multi-version legacy envs.
Code

scopes/harmony/aspect-loader/aspect-loader.main.runtime.ts[R248-251]

+  getLoadedAspectIdIgnoringVersion(idWithoutVersion: string): string | undefined {
+    return this.harmony.extensionsIds.find(
+      (extId) => extId.split('@')[0] === idWithoutVersion && Boolean(this.harmony.extensions.get(extId)?.loaded)
+    );
Evidence
AspectLoaderMain chooses the first loaded matching ID, and DependencyResolverMain relies on it as
the canonical legacy-core env ID. Meanwhile EnvsMain explicitly implements deterministic
multi-version selection (semver sort + warning), demonstrating multi-version legacy envs are a
handled case and that deterministic selection is desirable.

scopes/harmony/aspect-loader/aspect-loader.main.runtime.ts[237-251]
scopes/dependencies/dependency-resolver/dependency-resolver.main.runtime.ts[1574-1585]
scopes/envs/envs/environments.main.runtime.ts[301-327]

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

## Issue description
`getLoadedAspectIdIgnoringVersion()` uses `Array.find()` over `harmony.extensionsIds`, so when multiple versions of the same aspect/env are loaded it picks whichever was registered first. This is inconsistent with EnvsMain’s deterministic “pick highest version and warn” behavior for multi-version legacy-core envs.
### Issue Context
This function is used by dependency resolution to rewrite legacy-core env dependency IDs to a single canonical instance. If multiple loaded versions exist, order-dependent selection can lead to confusing/unstable behavior.
### Fix Focus Areas
- scopes/harmony/aspect-loader/aspect-loader.main.runtime.ts[237-251]
- scopes/dependencies/dependency-resolver/dependency-resolver.main.runtime.ts[1578-1585]
- scopes/envs/envs/environments.main.runtime.ts[304-327]

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


6. Legacy env failures suppressed 🐞 Bug ◔ Observability
Description
Workspace.getWorkspaceIssues() suppresses legacy-core env MODULE_NOT_FOUND failures by checking only
/node_modules/, but roots-based env resolution installs them under
node_modules/.bit_roots//node_modules/. This can hide real env load failures from bit status
even when the env package is actually installed (just not hoisted to root node_modules).
Code

scopes/workspace/workspace/workspace.ts[R1809-1812]

+          const envPackageName = getLegacyCoreEnvPackageName(failedIdWithoutVersion);
+          const isEnvPackageInstalled = fs.existsSync(path.join(this.path, 'node_modules', envPackageName));
+          // the missing module may be reported by its package name or by its absolute path in
+          // the workspace node_modules (when the require used a resolved path).
Evidence
The new suppression logic uses a root-node_modules-only existence check to decide whether to hide a
legacy-core env MODULE_NOT_FOUND error; however the workspace supports resolving envs from the roots
layout under node_modules/.bit_roots, so the env can be installed without existing at the checked
path.

scopes/workspace/workspace/workspace.ts[1800-1826]
scopes/workspace/workspace/types.ts[50-58]

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.getWorkspaceIssues()` suppresses certain legacy-core env load failures when it believes the env package is "not installed yet". The installation check only looks for `<workspace>/node_modules/<envPackageName>`, but with `resolveEnvsFromRoots` the env package may be installed under the roots layout (`node_modules/.bit_roots/...`). This can incorrectly suppress real failures from `bit status`.
### Issue Context
The suppression intends to hide the expected pre-`bit install` state, not hide failures when the env is actually present.
### Fix Focus Areas
- scopes/workspace/workspace/workspace.ts[1807-1818]
### What to change
- When deciding `isEnvPackageInstalled`, check both:
- `<workspace>/node_modules/<envPackageName>` (current)
- `<workspace>/node_modules/.bit_roots/<failedIdWithoutVersion>/node_modules/<envPackageName>` (roots layout, note that roots dir is keyed by versionless env id per comments in the resolver)
- Only suppress when **neither** location exists.
- Keep the existing `isEnvModuleNotFound` guard, but broaden it if needed to match the roots-path form as well.

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


7. Core errors overly suppressed 🐞 Bug ☼ Reliability
Description
WorkspaceAspectsLoader.resolveCoreAspectDefs() suppresses all core-aspect resolution errors when
throwOnError is false and only logs err.message, so non-transient core breakages (not just
missing dists) can be skipped during best-effort flows like install’s env reload. This can allow
commands to proceed with missing core aspects and fail later with less actionable errors.
Code

scopes/workspace/workspace/workspace-aspects-loader.ts[R286-289]

+        } catch (err: any) {
+          if (throwOnError) throw err;
+          this.logger.warn(`unable to resolve the core aspect "${coreId}", skipping it. ${err.message}`);
+          return undefined;
Evidence
The loader’s new helper explicitly catches all errors and skips the core aspect whenever
throwOnError is false. Install’s env reload path passes throwOnError: false into
resolveAspects, which then calls resolveCoreAspectDefs using that flag, so this broad
suppression can occur during common install flows.

scopes/workspace/workspace/workspace-aspects-loader.ts[266-294]
scopes/workspace/workspace/workspace-aspects-loader.ts[404-414]
scopes/workspace/install/install.main.runtime.ts[669-679]

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

## Issue description
`resolveCoreAspectDefs()` catches **any** error and skips the core aspect when `throwOnError` is false. The comment justifies this for an expected transient state (missing `dist` during install), but the implementation also suppresses unrelated failures (e.g. runtime errors, corrupted JS, invalid exports), reducing diagnosability and allowing later, harder-to-trace failures.
### Issue Context
Install explicitly calls `workspace.resolveAspects(..., { throwOnError: false })` during env reload. With the current broad catch, any core aspect failure in that path is reduced to a warning containing only `err.message`.
### Fix Focus Areas
- scopes/workspace/workspace/workspace-aspects-loader.ts[266-294]
- scopes/workspace/workspace/workspace-aspects-loader.ts[404-414]
- scopes/workspace/install/install.main.runtime.ts[669-679]
### Implementation guidance
- When `throwOnError` is false, suppress only the known/expected transient failures (e.g. MODULE_NOT_FOUND / missing dist entry) and rethrow other error types.
- Log richer context for suppressed errors (at least `err.stack` when available) to keep install debuggable.
- (Optional) Collect suppressed core-aspect failures and surface them as a structured non-blocking workspace issue so users can see which core aspects were skipped.

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


View review recommended (9)
8. In-flight loads not awaited 🐞 Bug ☼ Reliability
Description
WorkspaceAspectsLoader.loadAspects() (and the scope-side getManifestsGraphRecursively()) filters out
IDs that are already “in flight” and returns without waiting for the original load to finish, so
concurrent callers can proceed while the requested aspect/env is still not loaded.
Code

scopes/workspace/workspace/workspace-aspects-loader.ts[R130-133]

+    if (inFlightIds.length) {
+      this.logger.debug(`${loggerPrefix} skipping aspects that are already loading: ${inFlightIds.join(', ')}`);
+    }
+    notLoadedIds = idsToLoad;
Evidence
The workspace loader explicitly partitions requested IDs into inFlightIds and then drops them
(notLoadedIds = idsToLoad) without awaiting any existing load. The Workspace stores the
in-flight set globally and constructs a new WorkspaceAspectsLoader per call, so overlapping calls
share only the set (not a promise). The scope loader mirrors the same “skip” behavior.

scopes/workspace/workspace/workspace-aspects-loader.ts[121-144]
scopes/workspace/workspace/workspace.ts[210-215]
scopes/workspace/workspace/workspace.ts[2133-2157]
scopes/scope/scope/scope-aspects-loader.ts[115-151]

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 new in-flight cycle breaker uses a `Set` to detect currently-loading aspects and then *skips* those IDs. If `loadAspects()` is invoked concurrently (multiple `WorkspaceAspectsLoader` instances are created per call), a later caller can return before the in-flight aspect finishes loading, violating the implied contract of `await workspace.loadAspects(...)`.
## Issue Context
This guard is necessary to break *re-entrant/circular* chains, but it should not cause *concurrent* callers to observe “not loaded yet” aspects. A `Set` can only answer “is loading”, not “wait until loaded”.
## Fix Focus Areas
- scopes/workspace/workspace/workspace-aspects-loader.ts[121-145]
- scopes/workspace/workspace/workspace.ts[2133-2157]
- scopes/scope/scope/scope-aspects-loader.ts[115-152]
### Suggested approach
1. Replace/augment the `Set` with a `Map<string, Promise<...>>` (or `{ promise, ownerToken }`) keyed by `aspectLoadInFlightKey(id)`.
2. When an ID is already in-flight:
- If it belongs to the *current re-entrant chain* (cycle), keep the “skip to break the cycle” behavior.
- Otherwise, `await` the existing promise so concurrent callers observe the aspect as loaded upon return.
3. Ensure the promise entry is removed in `finally`, and that rejections propagate or are handled consistently with `throwOnError`.
(Using an owner token via AsyncLocalStorage or an explicit `loadContextId` parameter is one pragmatic way to distinguish re-entrant cycles from concurrency.)

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


9. Legacy dep not sanitized 🐞 Bug ☼ Reliability
Description
The PR removes several guardrails that previously stripped/rewrote @teambit/legacy, so any
workspace/component/env that still carries it can now leak it into dependency policies/manifests and
be handed to pnpm during bit install. This can break installs (or force unexpected registry
resolution) because @teambit/legacy is no longer treated as a special core/bvm-linked package
anywhere in the pipeline.
Code

scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts[L432-433]

-      // Remove bit bin from dep list
-      depList = depList.filter((dep) => dep.id !== '@teambit/legacy');
Evidence
Current code shows there is no longer any pnpm hook stripping @teambit/legacy, no manifest-level
filtering of it, and no override step that rewrites it into a peer. Additionally, forking now allows
all package deps through unconditionally, increasing the chance the legacy package enters saved
policies/manifests.

scopes/dependencies/pnpm/lynx.ts[612-629]
scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts[134-138]
scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts[423-434]
scopes/dependencies/dependencies/dependencies-loader/apply-overrides.ts[182-192]
scopes/component/forking/forking.main.runtime.ts[458-469]

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

## Issue description
`@teambit/legacy` is no longer filtered/rewritten across the dependency-manifest and dependency-policy pipeline. If a component/env/workspace policy still includes this package, it can now propagate into generated manifests and be sent to pnpm, leading to install failures or unexpected dependency graph shape.
### Issue Context
This PR intentionally stops linking `@teambit/legacy` as a core/bvm package, but it also removed the protective normalization that prevented stale manifests from introducing it into installs.
### Fix Focus Areas
Re-add a single, explicit compatibility filter (preferably centralized) that strips `@teambit/legacy` from:
- workspace/root policy filtering and component dependency lists used to generate manifests
- pnpm readPackage hooks (so transitive manifests also cannot re-introduce it)
- dependency-policy derivation paths that convert deps into policies (e.g. forking)
- scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts[134-138]
- scopes/dependencies/dependency-resolver/manifest/workspace-manifest-factory.ts[423-434]
- scopes/dependencies/pnpm/lynx.ts[612-629]
- scopes/dependencies/dependencies/dependencies-loader/apply-overrides.ts[182-192]
- scopes/component/forking/forking.main.runtime.ts[458-469]

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


10. Unbounded pnpm dir restores ✓ Resolved 🐞 Bug ➹ Performance
Description
restoreRemovedLoadedVirtualStoreDirs() performs Promise.all() over directory restores and runs
an fs.copy() per removed virtual-store dir, with no concurrency limit. In large installs (many
loaded packages / many re-keyed dirs), this can create a burst of deep parallel filesystem
operations that significantly slows installs and can fail under resource limits.
Code

scopes/dependencies/pnpm/preserve-loaded-virtual-store-dirs.ts[R91-94]

+  await Promise.all(
+    removed.map(async ({ dirName, dirPath, pkgName }) => {
+      const donorDirName = findDonorDirName(dirName, pkgName, currentDirs);
+      if (!donorDirName) {
Evidence
The restore helper explicitly uses Promise.all() over all removed dirs and performs fs.copy()
within each task; pnpm.package-manager invokes this helper after every install, so the unbounded
concurrency can directly impact the install critical path.

scopes/dependencies/pnpm/preserve-loaded-virtual-store-dirs.ts[73-115]
scopes/dependencies/pnpm/pnpm.package-manager.ts[200-269]

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

## Issue description
`restoreRemovedLoadedVirtualStoreDirs()` restores directories using `Promise.all` and performs potentially heavy recursive `fs.copy()` operations for each removed dir concurrently. This creates unbounded I/O and file-descriptor pressure during `bit install`.
### Issue Context
This restore runs after every pnpm install, so it sits on a hot path. Even if correctness is best-effort, the concurrency behavior can be the dominant cost or cause failures in constrained CI/container environments.
### Fix Focus Areas
- Replace `Promise.all(removed.map(... fs.copy ...))` with a bounded-concurrency mapper (e.g. `p-map` or a simple queue), and consider logging aggregate stats (removed count, restored count, duration).
- scopes/dependencies/pnpm/preserve-loaded-virtual-store-dirs.ts[73-115]
- scopes/dependencies/pnpm/pnpm.package-manager.ts[200-269]

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


11. Core link sync suppressed 🐞 Bug ☼ Reliability
Description
DependencyLinker.syncCoreAspectLinksForEnvs() now catches any error from the entire reconciliation
and only logs a warning, so unexpected failures (not just transient missing-dists) won’t fail the
install or be surfaced to the caller. This makes it easy to proceed with partially reconciled
core-aspect links and reduces diagnosability (no stack / no structured failure signal).
Code

scopes/dependencies/dependency-resolver/dependency-linker.ts[R673-676]

+    try {
+      await this.syncCoreAspectLinksForEnvsUnsafe(rootDir, componentIds);
+    } catch (err: any) {
+      this.logger.warn(`syncCoreAspectLinksForEnvs: skipped, ${err.message}`);
Evidence
The method now suppresses all thrown errors from the reconciliation, and it is invoked during
install cycles; suppressing unexpected failures here makes reconciliation silently best-effort even
when the failure is not the transient “missing dist” case described in comments.

scopes/dependencies/dependency-resolver/dependency-linker.ts[664-677]
scopes/workspace/install/install.main.runtime.ts[470-489]

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

## Issue description
`DependencyLinker.syncCoreAspectLinksForEnvs()` wraps the full sync in a blanket `try/catch` and proceeds after logging a warning. This suppresses unexpected failures (e.g. filesystem/permission errors, logic regressions) the same way as the intended transient `MODULE_NOT_FOUND` during mid-install, and it also loses stack/context.
### Issue Context
This method is called from the install flow in workspaces with `linkCoreAspects` disabled, so failures here can affect core-aspect bootstrapping/link reconciliation.
### Fix Focus Areas
- scopes/dependencies/dependency-resolver/dependency-linker.ts[664-678]
### What to change
- Catch only the specific, expected transient errors (e.g. `MODULE_NOT_FOUND` / `ERR_MODULE_NOT_FOUND` when core-aspect dists are temporarily missing).
- For all other errors: either rethrow (preferred) or return a structured failure (e.g. boolean/result) that the install flow can handle explicitly.
- Improve the warning to include enough diagnostics (at least `err.stack` when available) when you do intentionally suppress an expected transient error.

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


12. Fallback compiler can crash 🐞 Bug ☼ Reliability
Description
The new fallback env compiler throws if it can’t require('typescript'), so any code path that uses
the fallback default env can terminate instead of degrading gracefully. This is especially risky
because the fallback env is explicitly used as a safety net for early bootstrap and for components
whose env failed to load.
Code

scopes/envs/envs/fallback-typescript-compiler.ts[R20-23]

+    ts = require('typescript');
+  } catch {
+    throw new Error(
+      'the fallback compiler requires the "typescript" package, which is not installed. run "bit install" to install the component env'
Evidence
The fallback compiler explicitly throws when typescript cannot be required, and EnvsMain wires
this compiler into the fallback default env used for early bootstrap and env-load failures. The repo
root manifest does not itself guarantee typescript is present, so availability depends on
external/transitive installation context.

scopes/envs/envs/fallback-typescript-compiler.ts[17-25]
scopes/envs/envs/environments.main.runtime.ts[233-255]
package.json[59-65]

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

## Issue description
`getFallbackTypescriptCompiler()` hard-requires `typescript` at runtime and throws when it’s missing. This can crash Bit in scenarios where the fallback env is meant to keep the system operational (early bootstrap or when an env fails to load).
### Issue Context
The fallback env is used from `EnvsMain.getFallbackDefaultEnv()` as a safety net. If `typescript` is not present in the current node_modules resolution context, the fallback path becomes a hard failure.
### Fix Focus Areas
- scopes/envs/envs/fallback-typescript-compiler.ts[17-25]
- scopes/envs/envs/environments.main.runtime.ts[233-255]
### What to change
- Ensure `typescript` is guaranteed to be resolvable in all supported runtimes where fallback env can be invoked (e.g., add it as a runtime dependency of the package that ships `fallback-typescript-compiler`, or otherwise ensure it’s bundled/available).
- Alternatively (or additionally), make the fallback path truly best-effort: if `typescript` cannot be resolved, return a no-op compiler (or a clearer structured failure) that doesn’t crash unrelated commands, while still surfacing an actionable issue to the user.

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


13. Dependents search stops early 🐞 Bug ≡ Correctness
Description
Workspace.getDependentsIds() now stops traversing when it hits a non-workspace node, which can miss
workspace dependents reachable through scope-only components (e.g. A(ws) -> X(scope) -> B(ws),
querying dependents of B will not return A). This can cause includeDependents flows
(build/test/compile via getComponentsByUserInput) to silently skip required dependent components.
Code

scopes/workspace/workspace/workspace.ts[R752-755]

+        // when the node is filtered out, don't traverse through it (same semantics as
+        // graph.predecessors with a nodeFilter)
+        if (filterOutNowWorkspaceIds && !this.hasId(node.attr)) return;
+        dependents.push(node.attr);
Evidence
The new BFS explicitly returns before enqueuing a predecessor when it is not a workspace component,
so traversal cannot reach any workspace dependents behind that node. The graph builder shows that
scope-only components are inserted as nodes (via workspace.get), making such intermediate nodes
realistic. Existing code uses graph.predecessors() with a nodeFilter to find workspace dependents,
which would be undermined if filtered nodes also blocked traversal—indicating the new behavior is
likely a regression.

scopes/workspace/workspace/workspace.ts[733-757]
scopes/workspace/workspace/build-graph-from-fs.ts[205-213]
scopes/component/remove/remove.main.runtime.ts[222-229]

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.getDependentsIds()` was rewritten to iterative BFS, but it currently **returns early** for predecessors that are not workspace components when `filterOutNowWorkspaceIds` is true. This prunes traversal and can miss workspace dependents that are reachable only through scope-only nodes.
## Issue Context
The workspace dependency graph can contain non-workspace nodes because graph building loads deps via `workspace.get(depId)` (which can resolve from scope). The previous `graph.predecessors(..., { nodeFilter })` behavior is used elsewhere to find workspace dependents, implying filtering should exclude results but still allow traversal through intermediate nodes.
## Fix Focus Areas
- scopes/workspace/workspace/workspace.ts[733-758]
### Suggested change
Keep traversing (`queue.push(predecessorId)`) even when the predecessor node is filtered out; only add it to the returned `dependents` list when it passes the filter. This preserves “filter results” semantics without turning the filter into a traversal cutoff.

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


14. CI versions legacy envs 🐞 Bug ≡ Correctness
Description
attachEnvVersionToLaneConfig() rewrites a versionless env id into a versioned id when the lane
Version contains a matching env aspect-entry with a version, and adjustEnvsOnConfigObject() then
materializes it as a versioned config key. This can conflict with the workspace behavior that
legacy-core envs must remain configured versionless and can cause CI’s restoreLaneConfigChanges() to
persist a versioned env entry into .bitmap before tagging.
Code

scopes/git/ci/attach-env-version-to-lane-config.ts[R19-22]

+  const envVersion = envEntry?.extensionId?.version;
+  if (!envVersion) return;
+  laneConfig[EnvsAspect.id].env = `${envId}@${envVersion}`;
+  ExtensionDataList.adjustEnvsOnConfigObject(laneConfig);
Evidence
The CI helper explicitly attaches a version and then normalizes the config object into a versionless
env + versioned env-aspect entry; CI then persists that config into .bitmap. Separately, workspace
logic explicitly keeps legacy-core env IDs versionless in config, and legacy-core env IDs are
defined as envs that historically existed without versions.

scopes/git/ci/attach-env-version-to-lane-config.ts[15-22]
components/legacy/extension-data/extension-data-list.ts[230-239]
scopes/git/ci/ci.main.runtime.ts[1800-1820]
scopes/workspace/workspace/workspace.ts[2493-2505]
scopes/envs/envs/legacy-core-envs.ts[1-8]

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

## Issue description
`attachEnvVersionToLaneConfig()` upgrades a versionless `env` config to a versioned id (and then expands it into a versioned env-aspect key via `ExtensionDataList.adjustEnvsOnConfigObject`). This is correct for regular env-set cases, but it should **not** apply to legacy-core env IDs, which the workspace explicitly keeps versionless in config.
### Issue Context
- CI restore flow calls `attachEnvVersionToLaneConfig()` before writing `laneConfig` entries into `.bitmap`.
- `ExtensionDataList.adjustEnvsOnConfigObject()` will create a `laneConfig["<envId>@<version>"] = {}` entry once the env is rewritten to include a version.
- Workspace code explicitly preserves versionless config for legacy-core envs.
### Fix Focus Areas
- scopes/git/ci/attach-env-version-to-lane-config.ts[16-22]
### Suggested change
- Import `isLegacyCoreEnv` from `@teambit/envs`.
- Add an early return guard:
- after reading `envId` and before finding/attaching `envVersion`, do:
- `if (isLegacyCoreEnv(envId)) return;`
This keeps the CI restoration logic from pinning versions for legacy-core envs while preserving the intended behavior for regular envs.

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


15. Install masks env errors 🐞 Bug ☼ Reliability
Description
InstallMain.reloadOneAspectsGroup() treats broad module-not-found failures from an env/aspect
runtime provider as “not requirable yet” and only warns, so installs can succeed even when a
non-workspace env is genuinely broken (e.g. missing runtime dependency). This can leave the
workspace in a misleading “installed” state until later commands fail when the env is actually
needed.
Code

scopes/workspace/install/install.main.runtime.ts[R759-762]

+            err.code === 'ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING' ||
+            err.code === 'ERR_REQUIRE_ESM' ||
+            err.message?.includes('Cannot find module') ||
+            // a CJS dist evaluated as ESM (or vice versa) - happens when the package manager
Evidence
The provider call catches and suppresses MODULE_NOT_FOUND/Cannot find module errors and only
logs a warning, which can hide real runtime failures. The same reload path is invoked for aspects in
the scope group (not in the workspace), so this suppression applies to external envs/aspects too,
where such errors are not expected to be transient compilation gaps.

scopes/workspace/install/install.main.runtime.ts[744-770]
scopes/workspace/install/install.main.runtime.ts[787-799]

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

## Issue description
`InstallMain.reloadOneAspectsGroup()` suppresses provider failures for *all* reloaded aspects/envs when the error looks like module-not-found. This suppression is appropriate only for workspace aspects that may not be compiled yet during early install cycles; for non-workspace aspects (coming from scope/node_modules) a `MODULE_NOT_FOUND`/`Cannot find module` typically indicates a real broken env that should fail the install.
## Issue Context
The grouping logic explicitly creates a `scope` group for aspects not in the workspace, but the provider error suppression does not differentiate between workspace and non-workspace groups.
## Fix Focus Areas
- scopes/workspace/install/install.main.runtime.ts[744-772]
- scopes/workspace/install/install.main.runtime.ts[787-799]
## Suggested fix
1. Carry enough context into the `loadedPlugins` entries (e.g. `{ id, plugins, isWorkspace: group.workspace }`, optionally `localPath`).
2. In the provider `catch`, only apply the "not requirable yet" suppression when `group.workspace === true` (and possibly `group.envOfAspect === true`).
3. For `group.workspace === false` (scope/node_modules aspects), rethrow `MODULE_NOT_FOUND` / `Cannot find module` errors so `bit install` fails fast with an actionable message.
4. (Optional) If you still need some tolerance for non-workspace aspects, narrow it to very specific transient cases (e.g. ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING for a TS main) rather than the generic "Cannot find module" substring.

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


16. Hardcoded dist main path 🐞 Bug ≡ Correctness
Description
WorkspaceAspectsLoader.getDistMain() hardcodes the compiled main lookup to /dist/..., so the
Node 22 fallback can fail to find an existing compiled entry when an aspect/env compiler outputs to
a different distDir. In that case, requiring the aspect still fails even though compiled JS
exists, preventing aspect/env loading.
Code

scopes/workspace/workspace/workspace-aspects-loader.ts[R724-727]

+    const mainFile = component.state._consumer.mainFile;
+    if (!mainFile) return undefined;
+    const distMain = join(localPath, DEFAULT_DIST_DIRNAME, mainFile.replace(/\.(ts|tsx|mts|cts|jsx)$/, '.js'));
+    return fs.pathExistsSync(distMain) ? distMain : undefined;
Evidence
The fallback require path is computed only as /dist/.js and does not consult the component
compiler’s getDistPathBySrcPath(), even though the compiler contract supports arbitrary distDir
and path mapping. The aspect-loader already demonstrates the correct approach (use
getDistPathBySrcPath() when a compiler exists), so this omission can cause fallback loading to
miss the actual compiled output location.

scopes/workspace/workspace/workspace-aspects-loader.ts[679-727]
scopes/harmony/aspect-loader/aspect-loader.main.runtime.ts[173-224]
scopes/compilation/compiler/types.ts[45-49]
scopes/compilation/compiler/types.ts[119-123]

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

## Issue description
`WorkspaceAspectsLoader.getDistMain()` builds a fallback path using `DEFAULT_DIST_DIRNAME` (`dist`) and `mainFile.replace(... => .js)`. This ignores the compiler’s `getDistPathBySrcPath()` mapping, so aspects compiled to a non-`dist/` output directory won’t be found and the fallback require will still fail.
## Issue Context
`AspectLoaderMain.getAspectFilePath()` / `getRuntimePath()` already implement the correct pattern: try to obtain the component compiler and call `compiler.getDistPathBySrcPath(srcRelativePath)`, falling back to `DEFAULT_DIST_DIRNAME` only when the compiler isn't available.
## Fix Focus Areas
- scopes/workspace/workspace/workspace-aspects-loader.ts[679-728]
- scopes/harmony/aspect-loader/aspect-loader.main.runtime.ts[173-224]

ⓘ 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 add REVIEW.md to your repo root and Qodo follows it on every PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread scopes/workspace/workspace/workspace.ts Outdated
Comment thread scopes/envs/envs/fallback-typescript-compiler.ts
Comment thread scopes/workspace/workspace/workspace-aspects-loader.ts Outdated
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

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

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

qodo-free-for-open-source-projects Bot commented Jul 2, 2026

Copy link
Copy Markdown

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

Comment thread scopes/workspace/workspace/workspace-aspects-loader.ts
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

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

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

Copy link
Copy Markdown

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

Comment thread scopes/harmony/empty-env/empty-env.aspect.ts
Comment thread scopes/envs/envs/environments.main.runtime.ts
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 3f5c24e

Comment thread scopes/harmony/aspect/aspect.env.ts Outdated
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 0607c7d

Comment thread scopes/workspace/workspace/workspace.ts
Comment thread components/legacy/e2e-helper/e2e-env-helper.ts
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

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

Comment thread scopes/generator/generator/builtin-templates.ts
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 94eddce

Comment thread scopes/compilation/compiler/compiler.main.runtime.ts
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

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

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

Copy link
Copy Markdown

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

Comment thread scopes/workspace/workspace/workspace-aspects-manager.ts
Comment thread scopes/workspace/workspace/workspace.ts Outdated
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 66fd06b

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

qodo-free-for-open-source-projects Bot commented Jul 6, 2026

Copy link
Copy Markdown

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

Comment thread scopes/workspace/workspace/workspace-aspects-loader.ts Outdated
Comment thread scopes/workspace/workspace/workspace-aspects-loader.ts
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 684cdf6

Comment thread scopes/react/ui/docs/apply-providers/apply-providers.tsx Outdated
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 3df0fcd

Comment thread scopes/envs/envs/fallback-typescript-compiler.ts
Comment thread scopes/envs/envs/legacy-core-envs.ts Outdated
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

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

…the ones nobody claims

Three problems with the previous commit, all from review:

- BundleUiTask built both roots with `Promise.all`, which rejects as soon as one
  fails - the `finally` then closed compilers while the other root was still
  bundling, the exact hazard the deferral exists to avoid. It now waits with
  `allSettled` and surfaces the first failure afterwards.
- `build()` pushed every compiler it created onto `openBuildCompilers`, but only
  BundleUiTask ever drains that list. `bit start`'s pre-bundle and watch rebuilds
  call `build()` too, so a module graph per build would have stayed referenced for
  the life of the process - worse than before. Tracking is now opt-in via
  `deferClose`; every other caller gets its compilers closed in `build()`'s finally.
- Cleanup ran unguarded from a `finally`, so a throw from `compiler.close()` could
  replace the build error that sent us there. It is now per-compiler try/catch that
  logs and moves on, the same contract as the webpack bundler's cleanup.
…preview bundlers

Review point: the predicate was defined twice, once per bundler, so the two could
drift into handling TypeScript-in-node_modules differently. Both configs already
import from @teambit/webpack (and nothing in that aspect imports back), so it moves
there as a single export with the rationale attached.
…modules paths

Review point: the previous wording ('lets TypeScript through from anywhere, declaration
files excluded') could be read as excluding .d.ts globally. The predicate only ever
returns true for paths under node_modules; outside it, nothing is excluded and the
rule's own `test` decides.
…nent

The helper now lives in its own component, published as
@teambit/webpack.modules.exclude-node-modules-js. Both bundler configs import it
from there and the copy that lived in the webpack aspect is gone, along with its
re-export - nothing outside those two configs used it.

Pinned at 0.0.1 in the workspace policy. The lockfile is deliberately untouched:
regenerating it here rewrites ~130k lines of unrelated drift, which belongs in its
own chore commit.
Adds @teambit/webpack.modules.exclude-node-modules-js@0.0.1, pinned in the previous
commit's policy entry.

The diff is far larger than that one package: this is the first `bit install` to
reconcile the lockfile with the branch's `workspace.jsonc`, so it also drops entries
the branch had already removed - `@teambit/aspect` from the importers, and the
`@teambit/legacy@2.1.0` peer suffixes that trail through most resolutions.
Review: BundleUiTask found the first rejected root, threw its reason, and the catch
immediately replaced it with `new Error("Generating UI bundle failed")`. The actual
error only ever reached the debug log - diagnosing a failed BundleUI in CI meant
downloading the log artifact to find out what rspack actually said.

Now the thrown message names the roots that failed, every failing root is logged
(not only the one that ends the task), and the first reason is attached as `cause`.
The reason itself stays out of the message on purpose: for an rspack failure it is
the entire stats output, megabytes of it, which is why the message was generic in
the first place.
Same issue as BundleUI, found by review: `buildPreBundlePreview` creates an rspack
compiler, runs it, and never closes it - so the module graph and rspack native side
stay resident. Both callers keep the process alive well past that point: the
PreBundlePreview build task runs once per env inside a `bit build` that runs every
task in one process, and `bit start` pre-bundles on demand in a server that then
keeps running.

Closed in a `finally`, so the error paths release it too, and the close itself is
guarded - cleanup must not replace the build error that sent us there. No deferral
needed here, unlike BundleUiTask: there is one compiler and nothing else bundling
alongside it.

Checked the other rspack call site while here: `ui-server.ts` hands its compiler to
RspackDevServer, which needs it alive to watch, so it stays as is.
zkochan added a commit that referenced this pull request Aug 14, 2026
…ng on lane-only components (#10611)

A `bit ci pr` on a branch that had removed a component from the
workspace failed on
every run, in three successive places. All three come from the same gap:
a lane can
carry components the workspace does not track, and neither the switch
nor the sync
handled that.

### `--workspace-only` was accepted and never used

`LaneSwitcher` takes `existingOnWorkspaceOnly` and does nothing with it,
so
`bit lane switch --workspace-only` ("checkout only the components in the
workspace
to the selected lane") checked out everything on the lane, writing
components back
into `.bitmap` and onto disk. `bit ci pr` relies on that flag: there the
git
checkout is the source of truth and the lane only supplies version
pointers. Now
the switch filters its ids by `.bitmap` when the flag is set.

### `applyVersion` threw for a component that isn't on disk

Under `--force-ours`, `applyVersion` threw `applyVersion expect to get
componentFromFS` for a lane component absent from the workspace. "Keep
ours" only
means something when there are local files to keep; with none - which
`isLane`
explicitly allows - it now falls through and writes the component from
the model
instead of throwing.

### The config sync staged main's config for components that can't be
snapped

`syncConfigFromMain` wrote an unmerged entry for every lane component
whose head on
main had moved. `bit snap` collects during-merge components from those
scope-level
entries and resolves each against `.bitmap`, so an entry for a component
the
workspace doesn't track aborted the snap with `MissingBitMapComponent`.
The sync
exists to feed that snap, so a component that cannot be snapped is
skipped before
the model load.

### And the flag's other caller

Making `workspaceOnly` effective exposed that `bit ci sync`'s
`materializeLane`
inherited it from `switchToLane`'s defaults, where it is wrong:
mirroring a lane
onto a branch has to write the whole lane, including components this
workspace
doesn't track yet. `e2e/harmony/ci-sync.e2e.ts` covers exactly that with
a
lane-only `comp3`; it now passes `workspaceOnly: false`.

## Testing

Developed against #10465, where this set is what got `bit ci pr` past
the lane
switch and through to a successful snap and export of 171 components.
The full e2e
suite (40 shards, `ci-sync` and the lane suites included) has run green
on that
branch with these changes four consecutive times.
zkochan added a commit that referenced this pull request Aug 14, 2026
…ipt that resolves from node_modules (#10612)

Two independent problems in the bundling tasks, both found while getting
`bit ci pr`
through a build on #10465.

### The UI bundle compilers were never released

`BundleUiTask` bundles both UI roots (scope and workspace) and
`ui.build()` never
closed either rspack compiler. A compiler keeps its whole module graph
alive - plus
rspack's native side of it - and `bit build` runs every task in one
process, so both
UI graphs stayed resident through every task that follows, a preview
bundle per env
among them. The process was OOM-killed partway through those bundles.

The compilers are now closed once the task is done, rather than inside
`build()`.
The webpack bundler in this repo closes its compiler after each run the
same way.

Why at the end of the task and not per build, stated with the confidence
the
evidence actually supports: an earlier revision closed each compiler
inside
`build()`, and that run had the second root fail to resolve a module the
first had
resolved, while the same code with no closes bundled fine. But that run
was also
bundling a component's raw sources, because the component's `dist` was
missing -
a #10465-only condition (eight components had landed on a compiler-less
default
env) that has since been fixed. With `dist` present the bundle does not
walk into
component source directories at all, so I would not treat "closing
mid-task breaks
the other root" as established. Deferring is the conservative choice
while there
are two compilers in flight; it is not a constraint to design around.

Which matters for #10596: if the two roots become one rspack build with
two entry
points, there is no "other root still bundling", and the tracking added
here
(`openBuildCompilers` + `deferClose`) collapses into a single close in
`build()`'s
`finally`. This PR reduces what is held *after* the task; the peak
*during* it is
still two full compilations of the same app, which is that issue's
territory.

### `.ts` reaching the bundler unhandled

Both rspack configs excluded `node_modules` from the swc loader, so a
bundle that
reached TypeScript through `node_modules` died on the first `export
type` with
`Module parse failed`. That happens for real: a bit component consumed
through
`node_modules` can resolve to its sources, because an injected pnpm copy
is taken
from the package directory before the compile fills its `dist`, and the
package
entry then falls back to `index.ts`.

Excluding `node_modules` is about not re-processing already-transpiled
third-party
JavaScript. A `.ts` file is never valid bundler input wherever it
resolves from, so
the exclusion now applies to JavaScript only, with `.d.ts` still
excluded.

## Testing

On #10465 the compiler release is part of what took the measured
container peak of
the `bit ci pr` build from 16373MB to 11695MB (sampled from the cgroup,
16384MB
limit), and `BundleUI` succeeds with both changes in place. The full e2e
suite has
run green with them four consecutive times.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
zkochan and others added 3 commits August 14, 2026 14:47
Conflict resolutions:
- .circleci/config.yml: master's revert of --keep-lane (#10615) applied inside
  this branch's memory-sampler wrapper for the `bit ci pr` step.
- scopes/react/react/react.env.ts, react.main.runtime.ts: stay deleted - this
  branch removes the core env sources from the workspace, so #10610's edits to
  them are moot.
- scopes/preview/preview/rspack/rspack.config.ts: keep the mdxOptions parameter.
  The import master re-adds is what this branch removed on purpose:
  @teambit/mdx.modules.mdx-v3-options is ESM-only and has to reach the config
  through a native import() done by the caller.
- scopes/preview/preview/pre-bundle.ts: keep master's closeRspackCompiler, which
  logs a close failure instead of swallowing it (#10612), and drop this branch's
  earlier copy.
- workspace.jsonc: both sides - master's webpack-bundler/webpack-dev-server
  entries plus this branch's exclude-node-modules-js.
- pnpm-lock.yaml: regenerated from master's lockfile with `bit install`, so the
  full-re-resolution lockfile this branch carried is gone. @teambit/react is now
  the only added package (1.0.1099, consumed from the registry); everything else
  is importers and duplicate versions dropping out with the core env sources.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@teambit/node, react, aspect, env: 1.0.1042 -> 1.0.1102
@teambit/mdx, readme: 1.0.1043 -> 1.0.1103
@GiladShoham GiladShoham added the v3 prs to merge for bit v3 label Aug 16, 2026
…om-manifest

# Conflicts:
#	.bitmap
#	.circleci/config.yml
#	pnpm-lock.yaml
#	scopes/harmony/aspect/aspect.env.ts
#	scopes/react/react/apps/web/react-app-options.ts
#	scopes/react/react/apps/web/react.application.ts
#	scopes/react/react/apps/web/webpack/mutators.ts
#	scopes/react/react/react.env.ts
#	scopes/react/react/react.main.runtime.ts
#	scopes/react/react/webpack/webpack.config.env.dev.ts
@teambit/node, react, aspect, env: 1.0.1102 -> 1.0.1105
@teambit/mdx, readme: 1.0.1103 -> 1.0.1106

react/aspect no longer depend on @teambit/webpack after #10620.
… the core aspect

WebpackMain.createBundler/createDevServer, the WebpackBundler/WebpackDevServer
classes, and their config factories duplicated what @teambit/webpack.webpack-bundler
and @teambit/webpack.webpack-dev-server now do (react/aspect/node envs build
through those packages directly, not this aspect - see #10620). Nothing in the
codebase calls createBundler/createDevServer anymore.

Also drops generateExposePeersTransformer (already marked dead code) and its
only helpers (get-exposed-rules, resolve-peer).

Kept: the type exports, WebpackConfigMutator/Configuration re-exports, the
thin transformer/plugin/fallback re-exports, and the WebpackMain class itself
(gutted, so the aspect still loads for backward compatibility).
…apture

check_circular_dependencies was the only bit-executing job left on the
hub-stg (staging) default while bit_pr/bit_merge use production hub;
building the workspace graph here can trigger a live scope import of
uncached component objects, and staging appears to be the likely cause
of the sporadic 10m no-output CI hangs. Also add a script-level timeout
so a stall fails fast with a clear message, print the resolved bit
binary/version/hub_domain for diagnostics, and persist debug.log as a
CI artifact.
Mirrors e2e's --bit_bin. Lets us pin/compare which bit binary these
scripts run against (repo build vs a bvm-linked release) instead of
relying on whatever "bit" resolves to on PATH - useful for narrowing
down the CI hang, which turned out to reproduce locally too: the
repo's own binary took ~3x longer than a released bvm build on the
same workspace (5m50s vs 1m57s for `insights circular --json`).
An auto-merge with bit-ci's version-bump commit reintroduced 7
top-level .bitmap entries (aspect, babel, env, mdx, node, react,
readme) for legacy core envs this branch already removed from
tracking - their source no longer exists at those paths, so bbit
install failed with ComponentNotFoundInPath. The bot's commit predates
this branch's removal work, so a naive 3-way merge kept its stale
entries instead of the deletion. Removed exactly those 7 blocks;
confirmed via diff against merge-base/mine/bot that nothing else in
.bitmap needed touching (the remaining changes are the legitimate
2.0.80->2.0.81 version bump). bit status loads cleanly now.
…ce-cycle

Verified against two real CI runs and a local repro: execSync's timeout
error here doesn't set error.killed, it's a raw ETIMEDOUT from spawnSync,
so the clearer message never fired. Also correct the message itself -
the CI evidence points at a local force-write storm during aspect
loading (every dist file of every loaded aspect written to every
duplicate .pnpm-hash variant), not a network/registry stall.
Diagnostic addition to narrow down the perf regression found in this
check: run it once with the repo's own binary (gating, as before) and
once with the bvm-linked nightly release already fetched by
setup_harmony (informational only, never fails the job). Reuses the
existing install_bvm/bvm_upgrade commands and the same .bvm cache key
setup_harmony populates this pipeline run, so it should mostly reuse
the cached bundle rather than re-download. Remove once the regression
in the repo binary's aspect-loading path is root-caused.
CircleCI skips steps after a failed one unless that specific step is
marked when:always - it doesn't propagate through a whole reusable
command's inner steps. install_bvm/bvm_upgrade sat after the repo-bit
check step, so when that step failed/timed out (as expected), bvm
setup never ran and the diagnostic comparison silently no-opped
("bbit: command not found", swallowed by the || true). Move the bvm
setup earlier so it runs unconditionally, independent of the repo-bit
check's outcome.
We already know the repo-bit check is slow/fails; running the known-
good bvm baseline first gives that signal without waiting on the
repo-bit run, and it stays non-blocking (|| true) so it can't skip the
repo-bit gate either way.
Repo bit has a confirmed, reproducible perf regression on this check
(~5min timeout vs ~1-2min on a bvm release, across 4 environments) -
see the new CI-HANG-INVESTIGATION.md for the full root-cause writeup
(narrowed to workspace.ts's self-as-aspect recursion, unlocked by
isCoreAspect() now returning false for the envs this branch removed
from the core manifest). Disabling the repo-bit step until it's fixed
so it can't block merges on a broken check; the non-blocking bvm-bit
comparison stays as a sanity signal. Re-enable once fixed.
Runtime-instrumented the workspace.ts self-as-aspect branch in a
disposable /tmp clone - it's never entered during the hang, disproving
the earlier hypothesis. Re-reading the captured debug.log's INFO-level
loadAspects lines (not the much noisier DEBUG-level file-write lines)
shows the real shape: one single trace root recurses for the entire
5-minute window, alternating consumer-fs-load/extension-merge calls
into workspace.loadAspects without ever terminating - not fan-out
across many components.

Found a near-exact match already diagnosed and fixed (unmerged) on
origin/refactor/component-loading-v2-take-3-stage2: commits f9ae003
and its follow-up 1213c36 describe and fix the identical mechanism
in WorkspaceAspectsLoader.loadAspects (concurrent calls for different
root aspects independently re-isolating a shared env dependency,
because isAspectLoaded only flips true after a load completes). Their
fix serializes loadAspects through a single queue; measured similarly
(13:54 -> 10s on a 311-component workspace). Porting that approach is
now the primary recommended next step.
…om-manifest

# Conflicts:
#	.bitmap
#	pnpm-lock.yaml
@teambit/node, react, aspect, env: 1.0.1105 -> 1.0.1107
@teambit/mdx, readme: 1.0.1106 -> 1.0.1108
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v3 prs to merge for bit v3

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants