Skip to content

feat(runtime): reject incompatible runtime image reuse - #5096

Open
ScriptedAlchemy wants to merge 18 commits into
rfc5036/03-runtime-selectorsfrom
rfc5036/04-runtime-image-cache
Open

ScriptedAlchemy wants to merge 18 commits into
rfc5036/03-runtime-selectorsfrom
rfc5036/04-runtime-image-cache

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 18, 2026 •

Copy link
Copy Markdown
Member

Part of RFC #5036. An alternate proposal is open at #5128 with its own stack. This PR stays open until the decision on #5036 is made.
Stack: 4 of 7. Base: #5095 (rfc5036/03-runtime-selectors). Next: #5097.
Landing order: #5093 → #5094 → #5095 → #5096 → #5097 → #5098 → #5107.

What changes

Builds without module-federation:* conditions behave exactly as main. They register instances, reuse cached remote entries, and publish the external core with no new check or warning. A family that uses package conditions can no longer reuse a runtime instance, the external runtime-core, or a cached remote entry that was created for a different family, target, capability set, or entry loader. The remote-entry cache key that hooks see does not change.

createInstance attaches a RuntimeImageDescriptorV1 to each instance. Enhanced writes the descriptor only for families that use package conditions, and every check is inert without it. Rspack stays in legacy mode.

  • Before an instance is constructed, its family is compared with every registered instance. A mismatch fails before beforeInit hooks, global plugins, or registerShared run.
  • init() on an existing instance compares the full image before it merges options. The existing image must provide every capability the new image can use and must not expose one it forbids, so the result does not depend on load order.
  • The external runtime-core provider compares the full image before it reuses the published core.
  • The remote-entry cache stores a descriptor per cached promise. It compares family, target, entry loader, remote type, and global name when both sides carry an image. The URL policy is a per-call function and not part of the identity, so retry plugins can change it between attempts.

Deduplication, rejection cleanup, and the old rejection guard stay. Resetting a remote, or all federation state, removes the cache metadata.

Verification

  • The branch history was rewritten on 2026-09-26 to remove attribution trailers from commit messages only. Every tree and patch is identical to the verified head; f32aae69b is the rewritten tip.

  • Runtime-core passed 137 tests, runtime 97, and the retry plugin 20. The external provider passed 4 ESM and CommonJS tests.

  • Prettier and git diff --check pass.

  • CI at f32aae69b passed 20 checks, cancelled 4, and skipped 13.

  • The 4 cancelled checks are checkout-install and node20-compat in Build Affected Packages, lint-deps in Lint Dependencies, and publish-preview in pkg.pr.new Preview. Those three workflows each ran twice on this commit. They set cancel-in-progress: true per PR, so the second run cancelled the first about a second after it started. The second run of each workflow passed on the same commit. The cancelled runs were not rerun.

  • 12 skipped checks are the jobs of the cancelled Build Affected Packages run, which wait on checkout-install. e2e-treeshake was skipped in the second run because tools/scripts/ci-is-affected.mjs did not select it.

  • The shipped runtime-tools family declares "mode": "legacy-defines", so default builds write no descriptor. No end-to-end build exercises the mismatch paths.

Deviations from the RFC

None noted.

Overlaps with open PRs

File overlaps with 16 other open PRs

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@pkg-pr-new

pkg-pr-new Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@module-federation/devtools

pnpm add https://pkg.pr.new/@module-federation/devtools@f32aae6

@module-federation/cli

pnpm add https://pkg.pr.new/@module-federation/cli@f32aae6

create-module-federation

pnpm add https://pkg.pr.new/create-module-federation@f32aae6

@module-federation/dts-plugin

pnpm add https://pkg.pr.new/@module-federation/dts-plugin@f32aae6

@module-federation/enhanced

pnpm add https://pkg.pr.new/@module-federation/enhanced@f32aae6

@module-federation/error-codes

pnpm add https://pkg.pr.new/@module-federation/error-codes@f32aae6

@module-federation/esbuild

pnpm add https://pkg.pr.new/@module-federation/esbuild@f32aae6

@module-federation/managers

pnpm add https://pkg.pr.new/@module-federation/managers@f32aae6

@module-federation/manifest

pnpm add https://pkg.pr.new/@module-federation/manifest@f32aae6

@module-federation/metro

pnpm add https://pkg.pr.new/@module-federation/metro@f32aae6

@module-federation/metro-plugin-rnc-cli

pnpm add https://pkg.pr.new/@module-federation/metro-plugin-rnc-cli@f32aae6

@module-federation/metro-plugin-rnef

pnpm add https://pkg.pr.new/@module-federation/metro-plugin-rnef@f32aae6

@module-federation/metro-plugin-rock

pnpm add https://pkg.pr.new/@module-federation/metro-plugin-rock@f32aae6

@module-federation/modern-js

pnpm add https://pkg.pr.new/@module-federation/modern-js@f32aae6

@module-federation/modern-js-v3

pnpm add https://pkg.pr.new/@module-federation/modern-js-v3@f32aae6

@module-federation/native-federation-tests

pnpm add https://pkg.pr.new/@module-federation/native-federation-tests@f32aae6

@module-federation/native-federation-typescript

pnpm add https://pkg.pr.new/@module-federation/native-federation-typescript@f32aae6

@module-federation/nextjs-mf

pnpm add https://pkg.pr.new/@module-federation/nextjs-mf@f32aae6

@module-federation/node

pnpm add https://pkg.pr.new/@module-federation/node@f32aae6

@module-federation/observability-plugin

pnpm add https://pkg.pr.new/@module-federation/observability-plugin@f32aae6

@module-federation/playground

pnpm add https://pkg.pr.new/@module-federation/playground@f32aae6

@module-federation/retry-plugin

pnpm add https://pkg.pr.new/@module-federation/retry-plugin@f32aae6

@module-federation/rsbuild-plugin

pnpm add https://pkg.pr.new/@module-federation/rsbuild-plugin@f32aae6

@module-federation/rspack

pnpm add https://pkg.pr.new/@module-federation/rspack@f32aae6

@module-federation/rspress-plugin

pnpm add https://pkg.pr.new/@module-federation/rspress-plugin@f32aae6

@module-federation/rstest

pnpm add https://pkg.pr.new/@module-federation/rstest@f32aae6

@module-federation/runtime

pnpm add https://pkg.pr.new/@module-federation/runtime@f32aae6

@module-federation/runtime-core

pnpm add https://pkg.pr.new/@module-federation/runtime-core@f32aae6

@module-federation/runtime-tools

pnpm add https://pkg.pr.new/@module-federation/runtime-tools@f32aae6

@module-federation/sdk

pnpm add https://pkg.pr.new/@module-federation/sdk@f32aae6

@module-federation/storybook-addon

pnpm add https://pkg.pr.new/@module-federation/storybook-addon@f32aae6

@module-federation/third-party-dts-extractor

pnpm add https://pkg.pr.new/@module-federation/third-party-dts-extractor@f32aae6

@module-federation/treeshake-frontend

pnpm add https://pkg.pr.new/@module-federation/treeshake-frontend@f32aae6

@module-federation/treeshake-server

pnpm add https://pkg.pr.new/@module-federation/treeshake-server@f32aae6

@module-federation/typescript

pnpm add https://pkg.pr.new/@module-federation/typescript@f32aae6

@module-federation/utilities

pnpm add https://pkg.pr.new/@module-federation/utilities@f32aae6

@module-federation/webpack-bundler-runtime

pnpm add https://pkg.pr.new/@module-federation/webpack-bundler-runtime@f32aae6

@module-federation/bridge-react

pnpm add https://pkg.pr.new/@module-federation/bridge-react@f32aae6

@module-federation/bridge-react-webpack-plugin

pnpm add https://pkg.pr.new/@module-federation/bridge-react-webpack-plugin@f32aae6

@module-federation/bridge-shared

pnpm add https://pkg.pr.new/@module-federation/bridge-shared@f32aae6

@module-federation/bridge-vue3

pnpm add https://pkg.pr.new/@module-federation/bridge-vue3@f32aae6

@module-federation/inject-external-runtime-core-plugin

pnpm add https://pkg.pr.new/@module-federation/inject-external-runtime-core-plugin@f32aae6

commit: f32aae6

@ScriptedAlchemy
ScriptedAlchemy force-pushed the rfc5036/03-runtime-selectors branch from dbe2d5a to 2d4f5e6 Compare September 18, 2026 15:40
@ScriptedAlchemy
ScriptedAlchemy force-pushed the rfc5036/04-runtime-image-cache branch from 2d87526 to 92b0669 Compare September 18, 2026 15:50
@changeset-bot

changeset-bot Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f32aae6

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 48 packages
Name Type
@module-federation/enhanced Minor
@module-federation/runtime Minor
@module-federation/runtime-core Minor
@module-federation/inject-external-runtime-core-plugin Minor
@module-federation/modern-js-v3 Minor
@module-federation/modern-js Minor
@module-federation/nextjs-mf Patch
@module-federation/node Patch
@module-federation/rsbuild-plugin Minor
@module-federation/rspress-plugin Minor
@module-federation/rstest Minor
@module-federation/storybook-addon Patch
shared-tree-shaking-no-server-host Patch
shared-tree-shaking-no-server-provider Patch
@module-federation/devtools Minor
@module-federation/dts-plugin Minor
@module-federation/esbuild Patch
@module-federation/metro Minor
@module-federation/observability-plugin Patch
@module-federation/playground Patch
@module-federation/retry-plugin Minor
@module-federation/runtime-tools Minor
@module-federation/webpack-bundler-runtime Minor
@module-federation/bridge-react Minor
@module-federation/bridge-vue3 Minor
website-new Patch
@module-federation/rspack Minor
shared-tree-shaking-with-server-host Patch
shared-tree-shaking-with-server-provider Patch
node-dynamic-remote-new-version Patch
node-dynamic-remote Patch
remote5 Patch
remote6 Patch
@module-federation/metro-plugin-rnc-cli Minor
@module-federation/metro-plugin-rnef Minor
@module-federation/metro-plugin-rock Minor
@module-federation/cli Minor
create-module-federation Minor
@module-federation/error-codes Minor
@module-federation/managers Minor
@module-federation/manifest Minor
@module-federation/sdk Minor
@module-federation/third-party-dts-extractor Minor
@module-federation/treeshake-frontend Minor
@module-federation/treeshake-server Minor
@module-federation/bridge-react-webpack-plugin Minor
@module-federation/bridge-shared Minor
@module-federation/utilities Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@ScriptedAlchemy
ScriptedAlchemy force-pushed the rfc5036/03-runtime-selectors branch from 2d4f5e6 to 1b84bc8 Compare September 18, 2026 16:09
Attach runtime-image metadata before instances enter global state.

Reject known family, target, capability, loader, and URL-policy mismatches before reuse.

Keep legacy callers compatible and preserve the remote-entry cache key.
@ScriptedAlchemy
ScriptedAlchemy force-pushed the rfc5036/04-runtime-image-cache branch from 92b0669 to c919e89 Compare September 18, 2026 16:10
The selector checks only need the program result. Production minification
did not finish before the test runner reached its limit, so the assertion
never ran.
Compare remote-entry cache identity only when both the cached entry and the caller carry a
runtime image. Drop the URL policy from the identity so retry plugins can change it between
attempts, and stop storing the closure in global state.

Compare separately built instances by family only, and run that check before the constructor
runs beforeInit hooks, global plugins, and registerShared.

Remove the global default-instance pointer and the constructor-level image so loadRemote,
getInstance, and the debug constructor behave as on main.
Publish the external core again for the same name and version, warn only when a different
provider is already published, and stop warning when no runtime image exists. Drop the unread
entryLoadingIdentity field.
…mage-cache

# Conflicts:
#	packages/enhanced/src/lib/container/runtime/FederationRuntimePlugin.ts
Reusing a no-shared runtime for an image that can load shared modules
passed, while the reverse order failed. The current image must now provide
every capability the next image can use.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant