
























@@ -234,4 +234,85 @@ describe("ensureSkillsWatcher", () => {
234234changedPath: "/tmp/shared/demo/SKILL.md",
235235});
236236});
237+238+it("stops fanning a shared-directory change to a workspace after it unsubscribes", async () => {
239+vi.useFakeTimers();
240+const seen: SkillsChangeEvent[] = [];
241+refreshModule.registerSkillsChangeListener((change) => {
242+seen.push(change);
243+});
244+refreshModule.ensureSkillsWatcher({
245+workspaceDir: "/tmp/ws-a",
246+config: { skills: { load: { extraDirs: ["/tmp/shared"], watchDebounceMs: 10 } } },
247+});
248+refreshModule.ensureSkillsWatcher({
249+workspaceDir: "/tmp/ws-b",
250+config: { skills: { load: { extraDirs: ["/tmp/shared"], watchDebounceMs: 10 } } },
251+});
252+253+// ws-a turns watching off: it unsubscribes, but the shared watcher stays
254+// alive for ws-b (torn down only when the last subscriber leaves).
255+refreshModule.ensureSkillsWatcher({
256+workspaceDir: "/tmp/ws-a",
257+config: { skills: { load: { extraDirs: ["/tmp/shared"], watch: false } } },
258+});
259+260+const callPaths = (watchMock.mock.calls as unknown as Array<[string]>).map((call) => call[0]);
261+const sharedIndex = callPaths.findIndex((target) => target.includes("/tmp/shared"));
262+expect(sharedIndex).toBeGreaterThanOrEqual(0);
263+264+createdWatchers[sharedIndex]?.emit("change", "/tmp/shared/demo/SKILL.md");
265+await vi.advanceTimersByTimeAsync(10);
266+267+expect(seen).toContainEqual({
268+workspaceDir: "/tmp/ws-b",
269+reason: "watch",
270+changedPath: "/tmp/shared/demo/SKILL.md",
271+});
272+expect(seen.some((change) => change.workspaceDir === "/tmp/ws-a")).toBe(false);
273+});
274+275+it("rebuilds a shared watcher with last-writer debounce while preserving subscribers", async () => {
276+vi.useFakeTimers();
277+const seen: SkillsChangeEvent[] = [];
278+refreshModule.registerSkillsChangeListener((change) => {
279+seen.push(change);
280+});
281+refreshModule.ensureSkillsWatcher({
282+workspaceDir: "/tmp/ws-a",
283+config: { skills: { load: { extraDirs: ["/tmp/shared"], watchDebounceMs: 10 } } },
284+});
285+const callPaths1 = (watchMock.mock.calls as unknown as Array<[string]>).map((call) => call[0]);
286+const firstSharedIndex = callPaths1.findIndex((target) => target.includes("/tmp/shared"));
287+288+// ws-b subscribes to the same path with a different debounce: the shared
289+// watcher is rebuilt once, the previous instance closed, and both
290+// workspaces remain subscribed.
291+refreshModule.ensureSkillsWatcher({
292+workspaceDir: "/tmp/ws-b",
293+config: { skills: { load: { extraDirs: ["/tmp/shared"], watchDebounceMs: 50 } } },
294+});
295+296+expect(createdWatchers[firstSharedIndex]?.close).toHaveBeenCalledTimes(1);
297+const callPaths2 = (watchMock.mock.calls as unknown as Array<[string]>).map((call) => call[0]);
298+const sharedIndices = callPaths2
299+.map((target, index) => (target.includes("/tmp/shared") ? index : -1))
300+.filter((index) => index >= 0);
301+expect(sharedIndices).toHaveLength(2);
302+const liveSharedIndex = sharedIndices[sharedIndices.length - 1] ?? -1;
303+304+createdWatchers[liveSharedIndex]?.emit("change", "/tmp/shared/demo/SKILL.md");
305+await vi.advanceTimersByTimeAsync(50);
306+307+expect(seen).toContainEqual({
308+workspaceDir: "/tmp/ws-a",
309+reason: "watch",
310+changedPath: "/tmp/shared/demo/SKILL.md",
311+});
312+expect(seen).toContainEqual({
313+workspaceDir: "/tmp/ws-b",
314+reason: "watch",
315+changedPath: "/tmp/shared/demo/SKILL.md",
316+});
317+});
237318});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。