From 4f9169c6dd9324e19ddfc730e9eb26b02189af4c Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Wed, 22 Apr 2026 14:38:24 -0700 Subject: [PATCH] fix(hooks): avoid stale skill workshop startup fallback --- extensions/skill-workshop/index.test.ts | 47 +++++++++++++++++++ extensions/skill-workshop/index.ts | 6 ++- .../contracts/boundary-invariants.test.ts | 3 +- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/extensions/skill-workshop/index.test.ts b/extensions/skill-workshop/index.test.ts index d5e52ae0eca..d6074f539b0 100644 --- a/extensions/skill-workshop/index.test.ts +++ b/extensions/skill-workshop/index.test.ts @@ -324,6 +324,53 @@ describe("skill-workshop", () => { ).resolves.toBeUndefined(); }); + it("does not fall back to startup config when live skill-workshop config is removed", async () => { + const workspaceDir = await makeTempDir(); + const stateDir = await makeTempDir(); + let configFile: Record = {}; + let tool: AnyAgentTool | undefined; + let toolFactory: + | ((ctx: { workspaceDir?: string }) => AnyAgentTool | AnyAgentTool[] | null | undefined) + | undefined; + const api = createTestPluginApi({ + pluginConfig: { approvalPolicy: "auto" }, + runtime: { + agent: { + resolveAgentWorkspaceDir: () => workspaceDir, + }, + state: { + resolveStateDir: () => stateDir, + }, + config: { + loadConfig: () => configFile, + }, + } as never, + registerTool(registered) { + toolFactory = typeof registered === "function" ? registered : undefined; + const resolved = + typeof registered === "function" ? registered({ workspaceDir }) : registered; + tool = Array.isArray(resolved) ? resolved[0] : (resolved ?? undefined); + }, + }); + + plugin.register(api); + + const refreshedTool = toolFactory?.({ workspaceDir }); + tool = Array.isArray(refreshedTool) ? refreshedTool[0] : (refreshedTool ?? undefined); + + const result = await tool?.execute?.("call-1", { + action: "suggest", + skillName: "screenshot-asset-workflow", + description: "Screenshot asset workflow", + body: "Verify dimensions, optimize the PNG, and run the relevant gate.", + }); + + expect(result?.details).toMatchObject({ status: "pending" }); + await expect( + fs.access(path.join(workspaceDir, "skills", "screenshot-asset-workflow", "SKILL.md")), + ).rejects.toMatchObject({ code: "ENOENT" }); + }); + it("skips agent_end hook wiring when auto-capture is disabled", () => { const on = vi.fn(); const api = createTestPluginApi({ diff --git a/extensions/skill-workshop/index.ts b/extensions/skill-workshop/index.ts index ef9c71896d9..27136527de1 100644 --- a/extensions/skill-workshop/index.ts +++ b/extensions/skill-workshop/index.ts @@ -18,9 +18,11 @@ export default definePluginEntry({ return; } const resolveCurrentConfig = () => { - const runtimeConfig = api.runtime.config?.loadConfig?.(); + const runtimeConfigAvailable = typeof api.runtime.config?.loadConfig === "function"; + const runtimeConfig = runtimeConfigAvailable ? api.runtime.config.loadConfig() : undefined; const runtimePluginConfig = - resolvePluginConfigObject(runtimeConfig, "skill-workshop") ?? api.pluginConfig; + resolvePluginConfigObject(runtimeConfig, "skill-workshop") ?? + (!runtimeConfigAvailable ? api.pluginConfig : undefined); return resolveConfig(runtimePluginConfig); }; diff --git a/src/plugins/contracts/boundary-invariants.test.ts b/src/plugins/contracts/boundary-invariants.test.ts index ee6fbfec01a..2492a464d76 100644 --- a/src/plugins/contracts/boundary-invariants.test.ts +++ b/src/plugins/contracts/boundary-invariants.test.ts @@ -66,7 +66,8 @@ const BUNDLED_LIVE_CONFIG_HOOK_GUARDS = { ], "extensions/skill-workshop/index.ts": [ 'resolvePluginConfigObject(runtimeConfig, "skill-workshop")', - "api.runtime.config?.loadConfig?.()", + 'typeof api.runtime.config?.loadConfig === "function"', + "api.runtime.config.loadConfig()", ], "extensions/thread-ownership/index.ts": [ 'resolvePluginConfigObject(currentConfig, "thread-ownership")',