test(runner): check App icon parity at the server boundary

Keep both live and catalog schema assertions through public runner exports while removing the standalone suite dependency on App source.

Paperclip-Task: rich-acp-production

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-09-29 13:51:40 -05:00
1 parent 01d6fcb832
commit d16f56ef71
2 files changed
+25 -12

No files matched your search

@@ -20,18 +20,6 @@ const packageRoot = resolve(
);
describe("semantic action catalog", () => {
it("advertises only icons accepted by the project API on both tool surfaces", async () => {
const { PROJECT_ICON_NAMES } = await import("../../../shared/src/constants.js");
const ajv = new Ajv2020({ allErrors: true, allowUnionTypes: true, strict: true });
for (const schema of [createProjectAction.live.descriptor.inputSchema, paperclipSemanticAction("create_project")!.inputSchema]) {
const validate = ajv.compile(schema);
const input = { name: "Onboarding", idempotencyKey: "onboarding" };
expect(schema).toMatchObject({ properties: { icon: { enum: [...PROJECT_ICON_NAMES, null] } } });
for (const icon of [...PROJECT_ICON_NAMES, null]) expect(validate({ ...input, icon }), String(icon)).toBe(true);
expect(validate({ ...input, icon: "users" })).toBe(false);
}
});
it("limits project repository URLs to HTTPS GitHub repository paths on both tool surfaces", () => {
const ajv = new Ajv2020({ allErrors: true, allowUnionTypes: true, strict: true });
for (const schema of [createProjectAction.live.descriptor.inputSchema, paperclipSemanticAction("create_project")!.inputSchema]) {
@@ -0,0 +1,25 @@
import Ajv2020 from "ajv/dist/2020.js";
import { describe, expect, it } from "vitest";
import {
capabilitySemanticToolDescriptor,
paperclipSemanticAction,
} from "@paperclipai/paperclip-runner";
import { PROJECT_ICON_NAMES } from "@paperclipai/shared";
// Cross-package parity belongs at the App boundary: the standalone runner
// must not import the App's shared package, even from its own tests.
describe("runner project icon contract", () => {
it("advertises only icons accepted by the project API on both tool surfaces", () => {
const ajv = new Ajv2020({ allErrors: true, allowUnionTypes: true, strict: true });
for (const schema of [
capabilitySemanticToolDescriptor("create_project")!.inputSchema,
paperclipSemanticAction("create_project")!.inputSchema,
]) {
const validate = ajv.compile(schema);
const input = { name: "Onboarding", idempotencyKey: "onboarding" };
expect(schema).toMatchObject({ properties: { icon: { enum: [...PROJECT_ICON_NAMES, null] } } });
for (const icon of [...PROJECT_ICON_NAMES, null]) expect(validate({ ...input, icon }), String(icon)).toBe(true);
expect(validate({ ...input, icon: "users" })).toBe(false);
}
});
});