Skip to content

fix(contracts): use static imports for provider registry to fix vmForks jiti failures#49661

Closed
kimptoc wants to merge 1 commit into
openclaw:mainfrom
kimptoc:fix/runtime-contract-vmforks
Closed

fix(contracts): use static imports for provider registry to fix vmForks jiti failures#49661
kimptoc wants to merge 1 commit into
openclaw:mainfrom
kimptoc:fix/runtime-contract-vmforks

Conversation

@kimptoc
Copy link
Copy Markdown
Contributor

@kimptoc kimptoc commented Mar 18, 2026

Summary

Upstream (#4ac9024de) added static imports for all bundled plugins but loadBundledProviderRegistry() still uses resolvePluginProviders() (jiti-only). This PR updates loadBundledProviderRegistry() to use those static imports via capturePluginRegistration() first, with jiti as a supplement-only fallback.

Root causes fixed:

  • vmForks incompatibility: jiti's CJS transform tries to set require on the vitest vmForks VM context, which marks it read-only getter → TypeError: Cannot set property require. Static capturePluginRegistration() calls bypass jiti entirely.
  • vi.mock() divergence: jiti creates separate CJS module instances that bypass vitest's ES module mock intercept. Static ESM imports share the same instance that vi.mock() patches.

Key properties of the fix:

  • Per-plugin fault isolation: one register() failure skips only that plugin, others load normally
  • jiti fallback: succeededPluginIds tracks only plugins that actually succeeded static capture, so failures aren't also excluded from the jiti supplement
  • Graceful degradation: jiti errors are caught and stored in providerContractLoadError

CI evidence

src/plugins/contracts/runtime.contract.test.ts failures in Linux test shard 1:

main (before) This PR
runtime.contract.test.ts 22/27 failed 1/27 failed

The 1 remaining failure is an OOM timeout (pre-existing on main, unrelated to vmForks).

All other failures in the shard (runtime-web-tools, fal, command-secret-gateway, OOM) are identical on main — pre-existing and unrelated to this PR.

Test plan

  • pnpm test -- src/plugins/contracts/runtime.contract.test.ts --pool=vmForks passes all tests locally
  • CI: Linux test shard 1 reduced from 22 → 1 failures in runtime.contract.test.ts

Human Verification (required)

AI-generated PR — this PR was created with Claude Code assistance.

A human must verify:

  • The staticBundledProviderPlugins list covers all plugins statically imported at the top of registry.ts
  • loadBundledProviderRegistry() correctness: static entries first, jiti supplement for any missed
  • No provider IDs are duplicated or missing vs. the previous jiti-only approach

@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts size: XS labels Mar 18, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 18, 2026

Greptile Summary

This PR fixes 8 pre-existing test failures on Linux CI by resolving a jiti CJS-transform incompatibility with vitest's vmForks pool. The fix has two parts: registry.ts now seeds the provider registry from static imports for 10 key plugins (anthropic, google, github-copilot, qwen-portal-auth, etc.) before supplementing with jiti-loaded entries for the remaining ~23 plugins; and test-parallel.mjs moves runtime.contract.test.ts to the process-forks lane where jiti and vi.mock work correctly.

Key observations:

  • The deduplication guard (!staticPluginIdSet.has(provider.pluginId)) correctly prevents duplicate entries when a plugin appears in both the static list and the jiti-loaded list.
  • The new captureRegistrations helper closely mirrors the already-exported capturePluginRegistration utility in captured-registration.ts — reusing the existing helper would reduce divergence.
  • buildStaticProviderEntries() is called outside the try/catch block in loadBundledProviderRegistry, so a registration error from any of the 10 static plugins would crash the module initializer rather than being stored in providerContractLoadError as the jiti path does. In practice the risk is low (these are stable plugins with simple registerProvider calls), but it is an inconsistency with the existing error-handling contract.
  • The scripts/test-parallel.mjs change is minimal and well-commented.

Confidence Score: 4/5

  • Safe to merge — changes are test-infrastructure only, runtime/production behavior is unchanged, and the fix is well-targeted.
  • The approach is sound: static imports bypass the jiti/vmForks incompatibility, the deduplication logic is correct, and the pool change for the affected test file is appropriate. Two minor style-level concerns slightly reduce the score: buildStaticProviderEntries() sits outside the error boundary, and a near-duplicate helper was introduced instead of reusing the existing capturePluginRegistration utility. Neither is a blocking issue.
  • src/plugins/contracts/registry.ts — review the error-handling boundary around buildStaticProviderEntries() and the duplicate captureRegistrations helper.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/plugins/contracts/registry.ts
Line: 109-113

Comment:
**`captureRegistrations` duplicates existing `capturePluginRegistration` utility**

`captured-registration.ts` already exports a `capturePluginRegistration` helper that does exactly this — call `register` on a freshly created `CapturedPluginRegistration`:

```ts
export function capturePluginRegistration(params: {
  register(api: OpenClawPluginApi): void;
}): CapturedPluginRegistration { ... }
```

The new `captureRegistrations` function is functionally identical, just with a slightly different call signature. Consider reusing the existing utility to avoid divergence:

```suggestion
function buildStaticProviderEntries(): ProviderContractEntry[] {
  return staticBundledProviderPlugins.flatMap((plugin) => {
    const captured = capturePluginRegistration(plugin);
    return captured.providers.map((provider) => ({ pluginId: plugin.id, provider }));
  });
}
```

This would also let the standalone `captureRegistrations` function be removed entirely.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/plugins/contracts/registry.ts
Line: 116-119

Comment:
**Static provider build is outside the error boundary**

`buildStaticProviderEntries()` is called before the `try` block, so any exception thrown inside a plugin's `register()` call (e.g. an unexpected `undefined` during plugin boot) will propagate uncaught all the way up to the module-level initializer at line 146. That would crash the entire module, whereas the original code gracefully caught all jiti failures and stored them in `providerContractLoadError`.

Consider wrapping the static build in its own guard so partial failures are recoverable:

```suggestion
function loadBundledProviderRegistry(): ProviderContractEntry[] {
  let staticEntries: ProviderContractEntry[] = [];
  try {
    staticEntries = buildStaticProviderEntries();
  } catch (error) {
    providerContractLoadError = error instanceof Error ? error : new Error(String(error));
    return staticEntries;
  }
  const staticPluginIdSet = new Set(staticBundledProviderPlugins.map((p) => p.id));
```

This keeps the graceful-degradation contract that already exists for the jiti path.

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: "Merge branch 'main' ..."

@kimptoc kimptoc marked this pull request as draft March 18, 2026 08:42
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c55fca72ea

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/test-parallel.mjs Outdated
@kimptoc kimptoc force-pushed the fix/runtime-contract-vmforks branch from c55fca7 to 4db8211 Compare March 18, 2026 09:17
@kimptoc
Copy link
Copy Markdown
Contributor Author

kimptoc commented Mar 18, 2026

CI verification: fix confirmed working

Compared checks (node, test, 1, 2) shard results between this PR and main to verify the fix:

Test file main (pre-fix) This PR
src/plugins/contracts/runtime.contract.test.ts 22/27 failed 1/27 failed
src/secrets/runtime-web-tools.test.ts 10/15 failed 10/15 failed (pre-existing)
src/cli/command-secret-gateway.test.ts 2/23 failed 2/23 failed (pre-existing)
src/image-generation/providers/fal.test.ts 4/6 failed 4/6 failed (pre-existing)

The static import fix in registry.ts reduced runtime.contract.test.ts failures from 22 → 1. The one remaining failure ("owns anthropic 4.6 forward-compat resolution") ran for 146 seconds and is a timeout caused by the pre-existing OOM crash in this shard — not related to this PR's changes.

All other failures (runtime-web-tools, fal, command-secret-gateway, OOM) are identical on main and pre-date this PR.

The checks (node, contracts, pnpm test:contracts) job also fails identically on main — pre-existing failures unrelated to this PR.

@kimptoc kimptoc marked this pull request as ready for review March 18, 2026 10:56
Comment thread src/plugins/contracts/registry.ts Outdated
Comment thread src/plugins/contracts/registry.ts Outdated
@kimptoc kimptoc force-pushed the fix/runtime-contract-vmforks branch from 5a7f231 to edebd67 Compare March 18, 2026 12:40
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: edebd6754e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/plugins/contracts/registry.ts
Comment thread src/plugins/contracts/registry.ts Outdated
@kimptoc kimptoc force-pushed the fix/runtime-contract-vmforks branch 2 times, most recently from 46eb7fb to 5b1c0c8 Compare March 18, 2026 13:30
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5b1c0c8e2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/plugins/contracts/registry.ts Outdated
@kimptoc kimptoc force-pushed the fix/runtime-contract-vmforks branch from 5b1c0c8 to 37a71bc Compare March 18, 2026 14:00
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 37a71bc03f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/plugins/contracts/registry.ts Outdated
@kimptoc kimptoc force-pushed the fix/runtime-contract-vmforks branch from 37a71bc to 312569f Compare March 18, 2026 15:00
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 312569f609

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/plugins/contracts/registry.ts
@kimptoc kimptoc force-pushed the fix/runtime-contract-vmforks branch 2 times, most recently from 711c14e to cb7108c Compare March 18, 2026 19:11
@openclaw-barnacle openclaw-barnacle Bot removed the scripts Repository scripts label Mar 18, 2026
…istry

loadBundledProviderRegistry() now captures providers from all statically-imported
bundled plugins first (reliable in all vitest pool modes), then supplements with
jiti only for any plugins not covered. Key properties:

- Per-plugin isolation: one register() failure skips only that plugin; others load normally
- jiti fallback: failed static captures fall back to jiti (succeededPluginIds tracks only
  plugins that actually succeeded, so failures aren't also excluded from jiti)
- Graceful degradation: jiti errors are caught and stored in providerContractLoadError

Fixes vmForks incompatibility (jiti's CJS transform cannot set 'require' on the VM
context's read-only getter) and vi.mock() divergence (jiti creates separate CJS module
instances that bypass vitest's ES module mock intercept).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@kimptoc kimptoc force-pushed the fix/runtime-contract-vmforks branch from cb7108c to 2464aa7 Compare March 18, 2026 19:11
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2464aa7fab

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +144 to +148
const staticBundledProviderPlugins: RegistrablePlugin[] = [
amazonBedrockPlugin,
anthropicPlugin,
byteplusPlugin,
chutesPlugin,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Derive the static provider list from the real provider set

loadBundledProviderRegistry() now assumes staticBundledProviderPlugins mirrors bundledProviderPlugins, but the new array already diverges in this file: bundledProviderPlugins still includes falPlugin and does not include perplexityPlugin (src/plugins/contracts/registry.ts:431-466). That matters whenever this fallback path runs and resolvePluginProviders() throws, because the catch now returns only staticEntries; in that case fal disappears even though its module is already statically imported, while succeededPluginIds can suppress the wrong plugin IDs. Deriving this list from bundledProviderPlugins (or asserting the sets match) would keep the fallback from silently losing provider coverage.

Useful? React with 👍 / 👎.

@kimptoc
Copy link
Copy Markdown
Contributor Author

kimptoc commented Mar 18, 2026

as per contrib guidelines leaving for core team to address ci failures

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant