Skip to content

Commit dc67614

Browse files
committed
fix: include manifestRegistry in cache key
Address ClawSweeper P2 review finding: the WeakMap cache was keyed only on (config, env), ignoring manifestRegistry. Several gateway callers pass explicit registries, so the cache could return stale results when called with the same config/env but different registries. Cache is now a three-level structure: config → env → registry → result. Calls without a manifestRegistry use undefined as the Map key, ensuring they are cached separately from calls with an explicit registry. Added two new test cases: - recomputes when manifestRegistry reference changes - caches separately for calls with and without manifestRegistry
1 parent 4eb59b9 commit dc67614

2 files changed

Lines changed: 53 additions & 12 deletions

File tree

src/config/plugin-auto-enable.apply.test.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import { describe, expect, it, vi } from "vitest";
2+
import { afterEach } from "vitest";
3+
import type { PluginManifestRegistry } from "../plugins/manifest-registry.js";
24
import { applyPluginAutoEnable } from "./plugin-auto-enable.js";
3-
import { makeIsolatedEnv, resetPluginAutoEnableTestState } from "./plugin-auto-enable.test-helpers.js";
5+
import {
6+
makeIsolatedEnv,
7+
resetPluginAutoEnableTestState,
8+
} from "./plugin-auto-enable.test-helpers.js";
49
import type { OpenClawConfig } from "./types.openclaw.js";
5-
import { afterEach } from "vitest";
610

711
vi.mock("../channels/plugins/configured-state.js", async (importOriginal) => {
812
const actual = await importOriginal<typeof import("../channels/plugins/configured-state.js")>();
@@ -52,6 +56,31 @@ describe("applyPluginAutoEnable caching", () => {
5256
expect(result1).not.toBe(result2);
5357
});
5458

59+
it("recomputes when manifestRegistry reference changes", () => {
60+
const config: OpenClawConfig = {};
61+
const env = makeIsolatedEnv();
62+
const registry1: PluginManifestRegistry = { plugins: [], diagnostics: [] };
63+
const registry2: PluginManifestRegistry = { plugins: [], diagnostics: [] };
64+
const result1 = applyPluginAutoEnable({ config, env, manifestRegistry: registry1 });
65+
const result2 = applyPluginAutoEnable({ config, env, manifestRegistry: registry2 });
66+
// Different registry references should produce separate cache entries
67+
expect(result1).not.toBe(result2);
68+
expect(result1).toEqual(result2);
69+
});
70+
71+
it("caches separately for calls with and without manifestRegistry", () => {
72+
const config: OpenClawConfig = {};
73+
const env = makeIsolatedEnv();
74+
const registry: PluginManifestRegistry = { plugins: [], diagnostics: [] };
75+
const withoutRegistry = applyPluginAutoEnable({ config, env });
76+
const withRegistry = applyPluginAutoEnable({ config, env, manifestRegistry: registry });
77+
// Should be separate cache entries
78+
expect(withoutRegistry).not.toBe(withRegistry);
79+
// But same config/env/registry should hit cache
80+
const withRegistryAgain = applyPluginAutoEnable({ config, env, manifestRegistry: registry });
81+
expect(withRegistry).toBe(withRegistryAgain);
82+
});
83+
5584
it("cached calls are faster than uncached calls", () => {
5685
const env = makeIsolatedEnv();
5786
// First call with config1 — uncached

src/config/plugin-auto-enable.apply.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ export function materializePluginAutoEnableCandidates(params: {
4040
});
4141
}
4242

43-
const autoEnableCache = new WeakMap<object, WeakMap<object, PluginAutoEnableResult>>();
43+
const autoEnableCache = new WeakMap<
44+
object,
45+
WeakMap<object, Map<PluginManifestRegistry | undefined, PluginAutoEnableResult>>
46+
>();
4447

4548
export function applyPluginAutoEnable(params: {
4649
config?: OpenClawConfig;
@@ -50,19 +53,28 @@ export function applyPluginAutoEnable(params: {
5053
const config = params.config;
5154
const env = params.env;
5255
if (config && env) {
53-
let inner = autoEnableCache.get(config);
54-
if (inner) {
55-
const hit = inner.get(env);
56-
if (hit) {
57-
return hit;
56+
const registryKey = params.manifestRegistry;
57+
let envMap = autoEnableCache.get(config);
58+
if (envMap) {
59+
const registryMap = envMap.get(env);
60+
if (registryMap) {
61+
const hit = registryMap.get(registryKey);
62+
if (hit) {
63+
return hit;
64+
}
5865
}
5966
}
6067
const result = computeAutoEnable(params);
61-
if (!inner) {
62-
inner = new WeakMap();
63-
autoEnableCache.set(config, inner);
68+
if (!envMap) {
69+
envMap = new WeakMap();
70+
autoEnableCache.set(config, envMap);
6471
}
65-
inner.set(env, result);
72+
let registryMap = envMap.get(env);
73+
if (!registryMap) {
74+
registryMap = new Map();
75+
envMap.set(env, registryMap);
76+
}
77+
registryMap.set(registryKey, result);
6678
return result;
6779
}
6880
return computeAutoEnable(params);

0 commit comments

Comments
 (0)