Skip to content

plugins: suppress false duplicate-id warning across origins#42192

Closed
macarronesc wants to merge 1 commit intoopenclaw:mainfrom
macarronesc:fix/42099-plugin-duplicate-warning
Closed

plugins: suppress false duplicate-id warning across origins#42192
macarronesc wants to merge 1 commit intoopenclaw:mainfrom
macarronesc:fix/42099-plugin-duplicate-warning

Conversation

@macarronesc
Copy link
Copy Markdown

Summary

This PR fixes issue #42099 by refining plugin manifest deduplication logic to avoid false-positive duplicate ID warnings during gateway startup.

Root cause

The registry warned on duplicate plugin IDs too broadly, even when the same plugin ID appeared across different discovery origins as part of normal precedence/shadowing behavior.

What changed

  • Refactored duplicate tracking in the manifest registry to:
    • track discovered roots per plugin ID
    • track roots per origin for each plugin ID
  • Emit duplicate warnings only when distinct roots collide within the same origin bucket.
  • Keep cross-origin shadowing (config > workspace > global > bundled) silent.
  • Preserve precedence selection when the same physical root is discovered multiple times.

Tests

Updated and added tests in manifest-registry tests to cover:

  • warning on real duplicates within the same origin
  • no warning for distinct roots across different origins
  • existing precedence behavior for same physical plugin across origins

Safety and impact

  • Reduces startup warning noise
  • Keeps behavior unchanged for genuine duplicate conflicts
  • No API or manifest format changes

Fixes #42099

Copy link
Copy Markdown
Author

Implemented and validated.

I investigated the duplicate warning path in the plugin manifest registry and identified that duplicate diagnostics were too broad for cross-origin discovery scenarios. The fix now only warns when distinct plugin roots collide within the same origin bucket, while allowing expected cross-origin precedence behavior without noise.

Included test coverage updates to verify:

  • warning on true same-origin duplicate collisions
  • no warning on cross-origin distinct-root shadowing
  • unchanged precedence handling for same physical plugin discovery

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: 8fffec2f08

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +228 to 232
rootsForId.set(rootKey, {
candidate,
recordIndex: existingSameRoot.recordIndex,
});
}
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 Record promoted roots in the new origin bucket

When existingSameRoot is hit, the code updates rootsForId and immediately continues, but it never adds that root to originsForId for the current (possibly higher-precedence) origin. This causes missed duplicate diagnostics if ordering is bundled A -> global A (same root, promotion) -> global B (different root): hadOriginRoot is false for global B, so a real same-origin duplicate is silently suppressed. The previous logic warned in this case because the promoted candidate became the tracked seenIds entry.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 10, 2026

Greptile Summary

This PR refines the plugin manifest deduplication logic in loadPluginManifestRegistry to suppress false-positive duplicate-ID warnings that fire when the same plugin ID appears across different discovery origins (e.g. a bundled plugin shadowed by a config-referenced one). The change replaces a single seenIds map with two structures — seenRootsById for physical-root deduplication and seenOriginsById for per-origin collision tracking — and only emits a warning when two distinct roots collide within the same origin bucket.

Key observations:

  • The core fix is correct: cross-origin shadowing no longer emits spurious warnings, and same-origin distinct-root collisions still do.
  • The test update is necessary and well-targeted — the previous "emits duplicate warning" test was using bundled + global origins, which would have silently passed under the new logic without actually covering the intended same-origin case.
  • Logic gap in same-root promotion: When a same-root entry is promoted to a higher-precedence origin (e.g. root1 seen first as bundled then as config), seenOriginsById is not updated to move the root from the old origin's bucket into the new one. A subsequent different-root candidate arriving with the new origin (e.g. a second distinct config path for the same plugin ID) will see an empty origin bucket and emit no warning, silently violating the "warn on same-origin distinct-root collision" invariant. This is an edge case requiring both same-root cross-origin discovery AND a second independent path with the same higher-precedence origin, making it unlikely to affect most users, but it contradicts the stated invariant.

Confidence Score: 3/5

  • Safe for typical deployments; contains a logic gap in an uncommon multi-origin same-root promotion scenario that could cause missed duplicate warnings.
  • The core fix is correct and the main functionality works as intended—cross-origin shadowing is properly silent, and same-origin distinct-root collisions still warn. However, there is a verified logic gap: when a same physical root is promoted from a lower-precedence origin to a higher-precedence origin, the per-origin tracking (seenOriginsById) is not migrated. This can cause a missed warning if a separate root with the same plugin ID then arrives in the higher-precedence origin. This edge case requires both (1) the same physical path to appear in multiple discovery origins AND (2) a second independent path with the same plugin ID in the higher-precedence origin, making it unlikely to affect most users. The impact is reduced because the original root is still kept in the records (with correct precedence), but it violates the stated invariant about when warnings should be emitted.
  • src/plugins/manifest-registry.ts (lines 214–234): requires seenOriginsById migration when promoting a same-root entry to a higher-precedence origin.

Last reviewed commit: 8fffec2

Comment on lines +214 to +234
const existingSameRoot = rootsForId.get(rootKey);
if (existingSameRoot) {
// Same physical plugin discovered through multiple origins/sources.
// Keep only one record and pick the highest-precedence origin.
if (
PLUGIN_ORIGIN_RANK[candidate.origin] < PLUGIN_ORIGIN_RANK[existingSameRoot.candidate.origin]
) {
records[existingSameRoot.recordIndex] = buildRecord({
manifest,
candidate,
manifestPath: manifestRes.manifestPath,
schemaCacheKey,
configSchema,
});
rootsForId.set(rootKey, {
candidate,
recordIndex: existingSameRoot.recordIndex,
});
}
continue;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

seenOriginsById not updated on same-root origin promotion

When a same-root candidate is processed with a higher-precedence origin (e.g. a root already seen as bundled now appearing as config), rootsForId is correctly updated, but seenOriginsById is not adjusted. The old origin's set keeps the root, and the new origin's set never gains it.

This leads to a missed warning in the following scenario:

  1. Plugin X, root1, origin=bundledoriginsForId["bundled"] = {root1}, rootsForId[root1] = {bundled}
  2. Plugin X, root1, origin=config (same physical path) → existingSameRoot fires; record updated to config; rootsForId[root1] = {config}but originsForId["bundled"] still holds root1 and originsForId["config"] is still empty.
  3. Plugin X, root2, origin=config (different physical path) → existingSameRoot misses; rootsForOrigin = originsForId.get("config") = empty Set; hadOriginRoot = falseno warning emitted, even though two distinct config-origin roots now exist for the same ID.

This violates the stated invariant: "Warn only when distinct roots collide within the same origin bucket."

Suggested fix: when promoting a same-root entry to a higher-precedence origin, migrate the root from the old origin's bucket to the new one:

if (PLUGIN_ORIGIN_RANK[candidate.origin] < PLUGIN_ORIGIN_RANK[existingSameRoot.candidate.origin]) {
  // Migrate rootKey from the old origin bucket to the new one.
  originsForId.get(existingSameRoot.candidate.origin)?.delete(rootKey);
  const newOriginRoots = originsForId.get(candidate.origin) ?? new Set<string>();
  if (!originsForId.has(candidate.origin)) {
    originsForId.set(candidate.origin, newOriginRoots);
  }
  newOriginRoots.add(rootKey);

  records[existingSameRoot.recordIndex] = buildRecord({
    manifest,
    candidate,
    manifestPath: manifestRes.manifestPath,
    schemaCacheKey,
    configSchema,
  });
  rootsForId.set(rootKey, {
    candidate,
    recordIndex: existingSameRoot.recordIndex,
  });
}
continue;
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/plugins/manifest-registry.ts
Line: 214-234

Comment:
`seenOriginsById` not updated on same-root origin promotion

When a same-root candidate is processed with a higher-precedence origin (e.g. a root already seen as `bundled` now appearing as `config`), `rootsForId` is correctly updated, but `seenOriginsById` is not adjusted. The old origin's set keeps the root, and the new origin's set never gains it.

This leads to a missed warning in the following scenario:
1. Plugin X, `root1`, origin=`bundled``originsForId["bundled"] = {root1}`, `rootsForId[root1] = {bundled}`
2. Plugin X, `root1`, origin=`config` (same physical path) → `existingSameRoot` fires; record updated to config; `rootsForId[root1] = {config}`**but** `originsForId["bundled"]` still holds `root1` and `originsForId["config"]` is still empty.
3. Plugin X, `root2`, origin=`config` (different physical path) → `existingSameRoot` misses; `rootsForOrigin = originsForId.get("config")` = empty Set; `hadOriginRoot = false`**no warning emitted**, even though two distinct config-origin roots now exist for the same ID.

This violates the stated invariant: *"Warn only when distinct roots collide within the same origin bucket."*

Suggested fix: when promoting a same-root entry to a higher-precedence origin, migrate the root from the old origin's bucket to the new one:

```typescript
if (PLUGIN_ORIGIN_RANK[candidate.origin] < PLUGIN_ORIGIN_RANK[existingSameRoot.candidate.origin]) {
  // Migrate rootKey from the old origin bucket to the new one.
  originsForId.get(existingSameRoot.candidate.origin)?.delete(rootKey);
  const newOriginRoots = originsForId.get(candidate.origin) ?? new Set<string>();
  if (!originsForId.has(candidate.origin)) {
    originsForId.set(candidate.origin, newOriginRoots);
  }
  newOriginRoots.add(rootKey);

  records[existingSameRoot.recordIndex] = buildRecord({
    manifest,
    candidate,
    manifestPath: manifestRes.manifestPath,
    schemaCacheKey,
    configSchema,
  });
  rootsForId.set(rootKey, {
    candidate,
    recordIndex: existingSameRoot.recordIndex,
  });
}
continue;
```

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

@vincentkoc
Copy link
Copy Markdown
Member

ProjectClownfish could not safely update this branch, so it opened a narrow replacement PR instead.

Replacement PR: #72409
Source PR: #42192
Contributor credit is preserved in the replacement PR body and changelog plan.

@vincentkoc
Copy link
Copy Markdown
Member

ProjectClownfish could not safely update this branch, so it opened a narrow replacement PR instead.

Replacement PR: #72409
Source PR: #42192
Contributor credit is preserved in the replacement PR body and changelog plan.

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.

fix(plugins): false-positive duplicate plugin ID warning on gateway start (msteams)

2 participants