Skip to content

Plugins: suppress duplicate id warnings for same source paths#32564

Open
zwright8 wants to merge 1 commit intoopenclaw:mainfrom
zwright8:codex/pr26712-plugin-manifest-duplicate-warning
Open

Plugins: suppress duplicate id warnings for same source paths#32564
zwright8 wants to merge 1 commit intoopenclaw:mainfrom
zwright8:codex/pr26712-plugin-manifest-duplicate-warning

Conversation

@zwright8
Copy link
Copy Markdown

@zwright8 zwright8 commented Mar 3, 2026

Summary

Validation

  • pnpm exec vitest run src/plugins/manifest-registry.test.ts

Context

This PR is one focused slice extracted from:

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 3, 2026

Greptile Summary

This PR suppresses false-positive duplicate plugin-ID warnings when two PluginCandidate entries resolve to the same physical path, extending the existing rootDir realpath comparison with a lexical path.resolve fast-path and a new source-path equality check. Two issues need attention before merging:

  • Ineffective test coverage: The new test "suppresses duplicate warning when source path matches but realpath is unavailable" uses path.join(dir, "sub", "..") as an alternate rootDir, which normalizes back to dir — so the pathsMatch(rootDir, rootDir) check already returns true and the source-matching branch is never evaluated. Removing the pathsMatch(source, source) line would not cause the test to fail.
  • Overly broad source-path suppression: Treating matching source paths as sufficient proof of "same plugin" can suppress legitimate duplicate warnings when two distinct plugins happen to share the same entry-point file (e.g., aliased installs or shared cached sources). The rootDir is a more reliable identity signal than source.

Confidence Score: 3/5

  • Safe to merge with caveats — the core rootDir normalization improvement is correct, but the new source-path matching branch is untested and may over-suppress warnings.
  • The pathsMatch helper and its application to rootDir comparison are straightforward and well-motivated. The source-path equality branch introduces a subtle logic gap: the test written for it never actually exercises it, and the behavior it implements could falsely treat distinct plugins as duplicates in edge cases. These are not regressions on existing functionality, but the new feature is effectively unverified by the test suite.
  • src/plugins/manifest-registry.ts (lines 212–214) and src/plugins/manifest-registry.test.ts (lines 219–241) need closer review.

Last reviewed commit: 214cf0e

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +219 to +241
const candidates: PluginCandidate[] = [
{
idHint: "same-source-plugin",
source,
rootDir: dir,
origin: "bundled",
},
{
idHint: "same-source-plugin",
source,
rootDir: path.join(dir, "sub", ".."),
origin: "global",
},
];

const realpathSpy = vi.spyOn(fs, "realpathSync").mockImplementation(() => {
throw new Error("realpath unavailable");
});
try {
expect(countDuplicateWarnings(loadRegistry(candidates))).toBe(0);
} finally {
realpathSpy.mockRestore();
}
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.

Test doesn't exercise source-matching code path

The test is titled "suppresses duplicate warning when source path matches but realpath is unavailable", but it never actually reaches the pathsMatch(source, source) check.

The second candidate's rootDir is path.join(dir, "sub", ".."), which path.join normalizes to the same value as dir. So pathsMatch(existing.candidate.rootDir, candidate.rootDir) returns true first, short-circuiting the OR and never evaluating the source comparison. The realpathSync mock is equally unreachable since samePlugin is already true before safeRealpathSync is called.

To genuinely test the source-matching fallback, the two candidates need rootDirs that are lexically distinct (and do not resolve to the same path via path.resolve), while sharing the same source value. For example:

const dirA = makeTempDir();  // genuinely different temp dir
const dirB = makeTempDir();  // another genuinely different temp dir
writeManifest(dirA, manifest);
writeManifest(dirB, manifest);

const source = path.join(dirA, "index.ts");
fs.writeFileSync(source, "export default {};\n", "utf-8");

const candidates: PluginCandidate[] = [
  { idHint: "same-source-plugin", source, rootDir: dirA, origin: "bundled" },
  // Different rootDir but identical source — this is the case the new branch is meant to handle
  { idHint: "same-source-plugin", source, rootDir: dirB, origin: "global" },
];

As written, removing the pathsMatch(existing.candidate.source, candidate.source) line from the implementation would not cause this test to fail, which means the new feature is untested.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/plugins/manifest-registry.test.ts
Line: 219-241

Comment:
**Test doesn't exercise source-matching code path**

The test is titled "suppresses duplicate warning when source path matches but realpath is unavailable", but it never actually reaches the `pathsMatch(source, source)` check.

The second candidate's `rootDir` is `path.join(dir, "sub", "..")`, which `path.join` normalizes to the same value as `dir`. So `pathsMatch(existing.candidate.rootDir, candidate.rootDir)` returns `true` first, short-circuiting the OR and never evaluating the source comparison. The `realpathSync` mock is equally unreachable since `samePlugin` is already `true` before `safeRealpathSync` is called.

To genuinely test the source-matching fallback, the two candidates need rootDirs that are lexically distinct (and do not resolve to the same path via `path.resolve`), while sharing the same `source` value. For example:

```ts
const dirA = makeTempDir();  // genuinely different temp dir
const dirB = makeTempDir();  // another genuinely different temp dir
writeManifest(dirA, manifest);
writeManifest(dirB, manifest);

const source = path.join(dirA, "index.ts");
fs.writeFileSync(source, "export default {};\n", "utf-8");

const candidates: PluginCandidate[] = [
  { idHint: "same-source-plugin", source, rootDir: dirA, origin: "bundled" },
  // Different rootDir but identical source — this is the case the new branch is meant to handle
  { idHint: "same-source-plugin", source, rootDir: dirB, origin: "global" },
];
```

As written, removing the `pathsMatch(existing.candidate.source, candidate.source)` line from the implementation would not cause this test to fail, which means the new feature is untested.

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

Comment on lines +212 to +214
const samePath =
pathsMatch(existing.candidate.rootDir, candidate.rootDir) ||
pathsMatch(existing.candidate.source, candidate.source);
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.

Source-path equality may over-suppress valid duplicate warnings

Using pathsMatch(existing.candidate.source, candidate.source) as a standalone same-plugin signal is potentially too broad. Two genuinely distinct plugins can share the same entry-point source file while having different rootDirs (e.g., one plugin symlinks another's index.ts, or two package-manager-installed copies share a cached source). In those cases both plugins have the same manifest id AND the same source path, but they are not actually the same physical plugin — the duplicate warning should still fire.

The rootDir is the canonical identity of a plugin installation; the source file is an implementation detail that can be aliased. Consider restricting this branch to cases where both rootDir and source agree, or—at minimum—document the intentional trade-off in a comment so reviewers understand when this could produce a false negative.

const samePath =
  pathsMatch(existing.candidate.rootDir, candidate.rootDir) ||
  // Only treat matching sources as same-plugin when rootDirs are also
  // equivalent after resolution, to avoid suppressing warnings for
  // distinct plugins that happen to share an entry-point file.
  (pathsMatch(existing.candidate.source, candidate.source) &&
    path.resolve(existing.candidate.source).startsWith(
      path.resolve(existing.candidate.rootDir),
    ));
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/plugins/manifest-registry.ts
Line: 212-214

Comment:
**Source-path equality may over-suppress valid duplicate warnings**

Using `pathsMatch(existing.candidate.source, candidate.source)` as a standalone same-plugin signal is potentially too broad. Two genuinely distinct plugins can share the same entry-point source file while having different `rootDir`s (e.g., one plugin symlinks another's `index.ts`, or two package-manager-installed copies share a cached source). In those cases both plugins have the same manifest `id` AND the same `source` path, but they are not actually the same physical plugin — the duplicate warning should still fire.

The `rootDir` is the canonical identity of a plugin installation; the `source` file is an implementation detail that can be aliased. Consider restricting this branch to cases where both `rootDir` and `source` agree, or—at minimum—document the intentional trade-off in a comment so reviewers understand when this could produce a false negative.

```ts
const samePath =
  pathsMatch(existing.candidate.rootDir, candidate.rootDir) ||
  // Only treat matching sources as same-plugin when rootDirs are also
  // equivalent after resolution, to avoid suppressing warnings for
  // distinct plugins that happen to share an entry-point file.
  (pathsMatch(existing.candidate.source, candidate.source) &&
    path.resolve(existing.candidate.source).startsWith(
      path.resolve(existing.candidate.rootDir),
    ));
```

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

@zwright8 zwright8 force-pushed the codex/pr26712-plugin-manifest-duplicate-warning branch from 214cf0e to 7c6c532 Compare March 3, 2026 05:07
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