From 90a45ef25480f94d85f9ed4185afccfcd368f906 Mon Sep 17 00:00:00 2001 From: Dotta <34892728+cryppadotta@users.noreply.github.com> Date: Wed, 14 Oct 2026 18:01:52 -0500 Subject: [PATCH] perf(skills): skip inventory work for empty runtime selections (#16343) Return before company skill inventory work for a known empty native or stored-key selection. Preserve nonempty stored-key precedence, current access/version checks and legacy automatic skills. Verified head f3f89eedffe73593bd6871c653f6bc439073d180: 88 affected tests, full typecheck/build, full CI/E2E, Superagent, legacy status and Apex 5/5; no unresolved findings. Co-Authored-By: Paperclip --- doc/DEVELOPING.md | 4 +++ .../company-skills-empty-runtime.test.ts | 27 +++++++++++++++++++ server/src/services/company-skills.ts | 2 ++ 3 files changed, 33 insertions(+) create mode 100644 server/src/__tests__/company-skills-empty-runtime.test.ts diff --git a/doc/DEVELOPING.md b/doc/DEVELOPING.md index 7b07159f3b3..ea0ce2dad48 100644 --- a/doc/DEVELOPING.md +++ b/doc/DEVELOPING.md @@ -1895,6 +1895,10 @@ automatically alongside it; see [Agent commentary](agent-commentary.md). Other runtime skills remain controlled by `paperclipSkillSync.desiredSkills`. The native `paperclip_runner` does not use this legacy default because its protocol supplies the control-plane contract. +An empty native runtime selection returns before company skill inventory work. +An explicit empty stored key set does the same. Nonempty selections still use +the current inventory, version and access checks. Legacy automatic selections +still resolve their operational and feedback skills. Validate the catalog without writing the manifest: diff --git a/server/src/__tests__/company-skills-empty-runtime.test.ts b/server/src/__tests__/company-skills-empty-runtime.test.ts new file mode 100644 index 00000000000..369546e4e40 --- /dev/null +++ b/server/src/__tests__/company-skills-empty-runtime.test.ts @@ -0,0 +1,27 @@ +import { randomUUID } from "node:crypto"; +import { expect, it } from "vitest"; +import type { Db } from "@paperclipai/db"; +import { companySkillService } from "../services/company-skills.js"; + +const databaseAccess = new Error("Runtime selection touched the inventory database"); +const db = new Proxy({} as Db, { get() { throw databaseAccess; } }); + +it.each([ + { onlyKeys: new Set() }, + { onlyKeys: new Set(), includeLegacy: true, selectionConfig: { paperclipSkillSync: { desiredSkills: ["selected"] } } }, + { selectionConfig: {} }, + { selectionConfig: { paperclipSkillSync: { desiredSkills: [] } } }, + { selectionConfig: { paperclipSkillSync: { desiredSkills: [" ", null, {}] } } }, +])("does no inventory work for an empty runtime selection: %o", async options => { + await expect(companySkillService(db).listRuntimeSkillEntries(randomUUID(), options)).resolves.toEqual([]); +}); + +it.each([ + {}, + { includeLegacy: true, selectionConfig: {} }, + { includeLegacy: true, selectionConfig: { paperclipSkillSync: { desiredSkills: [] } } }, + { selectionConfig: { paperclipSkillSync: { desiredSkills: ["selected"] } } }, + { onlyKeys: new Set(["selected"]), selectionConfig: {} }, +])("retains inventory resolution for a requested or legacy skill set: %o", async options => { + await expect(companySkillService(db).listRuntimeSkillEntries(randomUUID(), options)).rejects.toBe(databaseAccess); +}); diff --git a/server/src/services/company-skills.ts b/server/src/services/company-skills.ts index c6da9f8bb86..0c4f5df645d 100644 --- a/server/src/services/company-skills.ts +++ b/server/src/services/company-skills.ts @@ -6143,6 +6143,8 @@ export function companySkillService(db: Db, options: { actor?: AuthorizationActo companyId: string, options: RuntimeSkillEntryOptions = {}, ): Promise { + if (options.onlyKeys?.size === 0 || (!options.onlyKeys && options.selectionConfig && !options.includeLegacy + && readPaperclipSkillSyncPreference(options.selectionConfig).desiredSkills.length === 0)) return []; const skills = await listFull(companyId); const selectedKeys = options.onlyKeys ?? (options.selectionConfig ? new Set( (options.includeLegacy ? resolveLegacyPaperclipDesiredSkillNames : resolvePaperclipDesiredSkillNames)(