Skip to content

Commit bfa867e

Browse files
openclaw-clownfish[bot]vincentkoc
authored andcommitted
fix(clownfish): address review for ghcrawl-207039-agentic-merge (1)
1 parent fc55964 commit bfa867e

3 files changed

Lines changed: 34 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ Docs: https://docs.openclaw.ai
606606
- Onboarding/GitHub Copilot: add manifest-owned `--github-copilot-token` support for non-interactive setup, including env fallback, tokenRef storage in ref mode, saved-profile reuse, and current Copilot default-model wiring. Refs #50002 and supersedes #50003. Thanks @scottgl9.
607607
- Gateway/install: add a validated `--wrapper`/`OPENCLAW_WRAPPER` service install path that persists executable LaunchAgent/systemd wrappers across forced reinstalls, updates, and doctor repairs instead of falling back to raw node/bun `ProgramArguments`. Fixes #69400. (#72445) Thanks @willtmc.
608608
- Plugins: fail plugin registration when loader-owned acceptance gates reject missing hook names or memory-only capability registration from non-memory plugins, surfacing the issue through plugin status and doctor instead of silently dropping the registration. Fixes #72459. Thanks @amknight.
609-
- Plugins: suppress false duplicate plugin-id warnings when the same id is discovered across different plugin origins, while preserving same-origin distinct-root diagnostics after same-root origin promotion. Fixes #42099; carries forward the focused #42192 approach and review feedback. Thanks @macarronesc.
609+
- Plugins: suppress only same-directory duplicate plugin-id false positives while keeping distinct-root cross-origin duplicate diagnostics visible. Fixes #42099; carries forward the focused #42192 approach and review feedback. Thanks @macarronesc.
610610
- macOS Gateway: write launchd services with a state-dir `WorkingDirectory`, use a durable state-dir temp path instead of freezing macOS session `TMPDIR`, create that temp directory before bootstrap, and label abort-shaped launchd exits as `SIGABRT/abort` in status output. Fixes #53679 and #70223; refs #71848. Thanks @dlturock, @stammi922, and @palladius.
611611
- Control UI/update: make `Update now` require a real gateway process replacement, report skipped/error update outcomes with stable reasons, and verify the running gateway version after restart so global installs cannot silently keep old code in memory. Fixes #62492; addresses #64892 and #63562. Thanks @IAMSamuelRodda.
612612
- Exec approvals: accept runtime-owned `source: "allow-always"` and `commandText` allowlist metadata in gateway and node approval-set payloads so Control UI round-trips no longer fail with `unexpected property 'source'`. Fixes #60000; carries forward #60064. Thanks @sd1471123, @sharkqwy, and @luoyanglang.

src/plugins/manifest-registry.test.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ describe("loadPluginManifestRegistry", () => {
372372
);
373373
});
374374

375-
it("lets config-loaded plugins replace bundled duplicates without cross-origin warnings", () => {
375+
it("warns when config-loaded plugins replace bundled distinct-root duplicates", () => {
376376
const bundledDir = makeTempDir();
377377
const configDir = makeTempDir();
378378
const manifest = { id: "config-shadow", configSchema: { type: "object" } };
@@ -392,12 +392,15 @@ describe("loadPluginManifestRegistry", () => {
392392
}),
393393
]);
394394

395-
expect(countDuplicateWarnings(registry)).toBe(0);
395+
expect(countDuplicateWarnings(registry)).toBe(1);
396396
expect(registry.plugins).toHaveLength(1);
397397
expect(registry.plugins[0]?.origin).toBe("config");
398+
const warning = registry.diagnostics.find((diag) => diag.pluginId === "config-shadow");
399+
expect(warning?.source).toBe(path.join(bundledDir, "index.ts"));
400+
expect(warning?.message).toContain(path.join(configDir, "index.ts"));
398401
});
399402

400-
it("keeps explicit installed globals as the effective duplicate winner without cross-origin warnings", () => {
403+
it("warns while keeping explicit installed globals as the effective duplicate winner", () => {
401404
const bundledDir = makeTempDir();
402405
const globalDir = makeTempDir();
403406
const manifest = { id: "zalouser", configSchema: { type: "object" } };
@@ -425,12 +428,16 @@ describe("loadPluginManifestRegistry", () => {
425428
],
426429
});
427430

428-
expect(countDuplicateWarnings(registry)).toBe(0);
431+
expect(countDuplicateWarnings(registry)).toBe(1);
429432
expect(registry.plugins).toHaveLength(1);
430433
expect(registry.plugins[0]?.origin).toBe("global");
434+
expectRegistryDiagnosticContains(
435+
registry,
436+
"bundled plugin will be overridden by global plugin",
437+
);
431438
});
432439

433-
it("suppresses duplicate warnings when the installed global is discovered before bundled", () => {
440+
it("warns when the installed global is discovered before bundled", () => {
434441
const bundledDir = makeTempDir();
435442
const globalDir = makeTempDir();
436443
const manifest = { id: "zalouser", configSchema: { type: "object" } };
@@ -458,9 +465,13 @@ describe("loadPluginManifestRegistry", () => {
458465
],
459466
});
460467

461-
expect(countDuplicateWarnings(registry)).toBe(0);
468+
expect(countDuplicateWarnings(registry)).toBe(1);
462469
expect(registry.plugins).toHaveLength(1);
463470
expect(registry.plugins[0]?.origin).toBe("global");
471+
expectRegistryDiagnosticContains(
472+
registry,
473+
"bundled plugin will be overridden by global plugin",
474+
);
464475
});
465476

466477
it("preserves provider auth env metadata from plugin manifests", () => {
@@ -1505,24 +1516,26 @@ describe("loadPluginManifestRegistry", () => {
15051516

15061517
it.each([
15071518
{
1508-
name: "keeps bundled plugins as the duplicate winner for auto-discovered globals",
1519+
name: "reports bundled plugins as the duplicate winner for auto-discovered globals",
15091520
registry: () =>
15101521
createDuplicateCandidateRegistry({
15111522
pluginId: "feishu",
15121523
duplicateOrigin: "global",
15131524
}),
1525+
expectedMessage: "global plugin will be overridden by bundled plugin",
15141526
},
15151527
{
1516-
name: "keeps bundled plugins as the duplicate winner for workspace duplicates",
1528+
name: "reports bundled plugins as the duplicate winner for workspace duplicates",
15171529
registry: () =>
15181530
createDuplicateCandidateRegistry({
15191531
pluginId: "shadowed",
15201532
duplicateOrigin: "workspace",
15211533
}),
1534+
expectedMessage: "workspace plugin will be overridden by bundled plugin",
15221535
},
1523-
] as const)("$name", ({ registry: buildRegistry }) => {
1536+
] as const)("$name", ({ registry: buildRegistry, expectedMessage }) => {
15241537
const registry = buildRegistry();
1525-
expect(countDuplicateWarnings(registry)).toBe(0);
1538+
expectRegistryDiagnosticContains(registry, expectedMessage);
15261539
expect(registry.plugins).toHaveLength(1);
15271540
expect(registry.plugins[0]?.origin).toBe("bundled");
15281541
});

src/plugins/manifest-registry.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,11 +744,21 @@ export function loadPluginManifestRegistry(
744744
installRecords: getInstallRecords(),
745745
});
746746
const candidateWins = candidateRank < existingRank;
747+
const winnerCandidate = candidateWins ? candidate : existing.candidate;
748+
const overriddenCandidate = candidateWins ? existing.candidate : candidate;
747749
if (candidateWins) {
748750
records[existing.recordIndex] = record;
749751
seenIds.set(manifest.id, { candidate, recordIndex: existing.recordIndex });
750752
pushManifestCompatibilityDiagnostics({ record, diagnostics });
751753
}
754+
if (winnerCandidate.origin !== overriddenCandidate.origin) {
755+
diagnostics.push({
756+
level: "warn",
757+
pluginId: manifest.id,
758+
source: overriddenCandidate.source,
759+
message: `duplicate plugin id detected; ${overriddenCandidate.origin} plugin will be overridden by ${winnerCandidate.origin} plugin (${winnerCandidate.source})`,
760+
});
761+
}
752762
continue;
753763
}
754764

0 commit comments

Comments
 (0)