diff --git a/packages/workflow-register/__tests__/build-descriptor.test.ts b/packages/workflow-register/__tests__/build-descriptor.test.ts new file mode 100644 index 0000000..d5056fd --- /dev/null +++ b/packages/workflow-register/__tests__/build-descriptor.test.ts @@ -0,0 +1,53 @@ +import { describe, expect, test } from "bun:test"; +import { + END, + START, + type ModeratorTable, + type WorkflowDefinition, +} from "@uncaged/workflow-protocol"; +import * as z from "zod/v4"; +import { buildDescriptor } from "../src/bundle/build-descriptor.js"; + +const phaseSchema = z.object({ + hash: z.string().meta({ "x-cas-ref": true }), + title: z.string(), +}); + +type TestMeta = { + planner: { phases: Array<{ hash: string; title: string }>; label: string }; +}; + +const testTable: ModeratorTable = { + [START]: [{ condition: "FALLBACK", role: "planner" }], + planner: [{ condition: "FALLBACK", role: END }], +}; + +describe("buildDescriptor", () => { + test("preserves x-cas-ref in role JSON Schema", () => { + const def: WorkflowDefinition = { + description: "test workflow", + roles: { + planner: { + description: "plans work", + systemPrompt: "plan", + schema: z.object({ + phases: z.array(phaseSchema), + label: z.string(), + }), + }, + }, + table: testTable, + }; + + const descriptor = buildDescriptor(def); + const props = (descriptor.roles.planner.schema as { properties: Record }) + .properties; + const phaseProps = ( + (props.phases as { items: { properties: Record } }).items + ).properties; + + expect((phaseProps.hash as Record)["x-cas-ref"]).toBe(true); + expect((phaseProps.title as Record)["x-cas-ref"]).toBeUndefined(); + expect((props.label as Record)["x-cas-ref"]).toBeUndefined(); + }); +}); diff --git a/packages/workflow-register/package.json b/packages/workflow-register/package.json index 39976f8..dea6e94 100644 --- a/packages/workflow-register/package.json +++ b/packages/workflow-register/package.json @@ -29,6 +29,9 @@ "zod": "^4.0.0", "typescript": "^5.8.3" }, + "scripts": { + "test": "bun test" + }, "publishConfig": { "access": "public" } diff --git a/packages/workflow-runtime/__tests__/collect-cas-refs.test.ts b/packages/workflow-runtime/__tests__/collect-cas-refs.test.ts index dbdfc2b..f315bf5 100644 --- a/packages/workflow-runtime/__tests__/collect-cas-refs.test.ts +++ b/packages/workflow-runtime/__tests__/collect-cas-refs.test.ts @@ -3,7 +3,7 @@ import * as z from "zod/v4"; import { collectCasRefs } from "../src/collect-cas-refs.js"; const phaseSchema = z.object({ - hash: z.string().meta({ casRef: true }), + hash: z.string().meta({ 'x-cas-ref': true }), title: z.string(), }); @@ -19,9 +19,9 @@ const plannerMetaSchema = z.discriminatedUnion("status", [ ]); describe("collectCasRefs", () => { - test("1. flat field with casRef annotation", () => { + test("1. flat field with x-cas-ref annotation", () => { const schema = z.object({ - completedPhase: z.string().meta({ casRef: true }), + completedPhase: z.string().meta({ 'x-cas-ref': true }), }); expect(collectCasRefs(schema, { completedPhase: "BHAAAAAAAAAAA" })).toEqual(["BHAAAAAAAAAAA"]); }); @@ -29,7 +29,7 @@ describe("collectCasRefs", () => { test("2. plain string without annotation is ignored", () => { const schema = z.object({ summary: z.string(), - completedPhase: z.string().meta({ casRef: true }), + completedPhase: z.string().meta({ 'x-cas-ref': true }), }); expect( collectCasRefs(schema, { @@ -76,8 +76,8 @@ describe("collectCasRefs", () => { test("5. null and undefined annotated fields are skipped", () => { const schema = z.object({ - ref: z.string().meta({ casRef: true }).nullable(), - optionalRef: z.string().meta({ casRef: true }).optional(), + ref: z.string().meta({ 'x-cas-ref': true }).nullable(), + optionalRef: z.string().meta({ 'x-cas-ref': true }).optional(), }); expect(collectCasRefs(schema, { ref: null, optionalRef: undefined })).toEqual([]); expect(collectCasRefs(schema, { ref: "BH55555555555", optionalRef: undefined })).toEqual([ @@ -89,7 +89,7 @@ describe("collectCasRefs", () => { const schema = z.object({ label: z.string(), phase: z.object({ - hash: z.string().meta({ casRef: true }), + hash: z.string().meta({ 'x-cas-ref': true }), title: z.string(), }), tags: z.array(z.string()), diff --git a/packages/workflow-runtime/src/collect-cas-refs.ts b/packages/workflow-runtime/src/collect-cas-refs.ts index 59e7e14..27c2b8a 100644 --- a/packages/workflow-runtime/src/collect-cas-refs.ts +++ b/packages/workflow-runtime/src/collect-cas-refs.ts @@ -6,7 +6,7 @@ type DefPipeIn = { in: ZodSchema }; function hasCasRef(schema: ZodSchema): boolean { const meta = z.globalRegistry.get(schema); - return meta !== undefined && meta.casRef === true; + return meta !== undefined && meta["x-cas-ref"] === true; } function walkOptional(schema: z.ZodOptional, data: unknown): string[] { @@ -116,7 +116,7 @@ function walkCasRefs(schema: ZodSchema, data: unknown): string[] { } } -/** Collect CAS content hashes from meta using `casRef` annotations on the Zod schema. */ +/** Collect CAS content hashes from meta using `x-cas-ref` annotations on the Zod schema. */ export function collectCasRefs(schema: ZodSchema, data: unknown): string[] { return walkCasRefs(schema, data); }