fix: enforce console schema contract safeguards

This commit is contained in:
lda
2026-08-12 14:31:22 +07:00 Verified
parent 20e6ecc117
commit e0e1b718b8
9 changed files with 193 additions and 26 deletions
@@ -262,18 +262,22 @@ const TraceFrameSchema = Schema.Struct({
state_changes: JsonObjectSchema,
});
const StructuralPathPartsSchema = Schema.Array(PathSegmentSchema).pipe(
Schema.filter((parts) => parts.length > 0),
);
const InputPathBindingSchema = Schema.Struct({
path: Schema.Union(
Schema.String,
Schema.Struct({
parts: Schema.Array(PathSegmentSchema),
parts: StructuralPathPartsSchema,
root: Schema.Literal("input", "state", "context"),
}),
),
target: Schema.Union(
Schema.String,
Schema.Struct({
parts: Schema.Array(PathSegmentSchema),
parts: StructuralPathPartsSchema,
root: Schema.Literal("local"),
}),
),
@@ -283,7 +287,7 @@ const InputValueBindingSchema = Schema.Struct({
target: Schema.Union(
Schema.String,
Schema.Struct({
parts: Schema.Array(PathSegmentSchema),
parts: StructuralPathPartsSchema,
root: Schema.Literal("local"),
}),
),
@@ -295,22 +299,18 @@ const InputBindingSchema = Schema.Union(
InputValueBindingSchema,
);
const StatePathPartsSchema = Schema.Array(PathSegmentSchema).pipe(
Schema.filter((parts) => parts.length > 0),
);
const OutputBindingSchema = Schema.Struct({
source: Schema.Union(
Schema.String,
Schema.Struct({
parts: Schema.Array(PathSegmentSchema),
parts: StructuralPathPartsSchema,
root: Schema.Literal("local"),
}),
),
target: Schema.Union(
Schema.String,
Schema.Struct({
parts: StatePathPartsSchema,
parts: StructuralPathPartsSchema,
root: Schema.Literal("state"),
}),
),
@@ -1100,6 +1100,49 @@ describe("authored RPC and manifest schema parity", () => {
}
});
it("rejects structural binding paths without any segments", () => {
const inputPayload = {
workspace_id: "console.demo",
revision: 3,
step_id: "render",
};
const outputPayload = { ...inputPayload, revision: 4 };
const cases = [
{
schema: WorkflowDraftWorkspacesSetStepInputBindingsPayloadSchema,
payload: {
...inputPayload,
bindings: [{ path: { root: "input", parts: [] }, target: "title" }],
},
},
{
schema: WorkflowDraftWorkspacesSetStepInputBindingsPayloadSchema,
payload: {
...inputPayload,
bindings: [{ path: "input.title", target: { root: "local", parts: [] } }],
},
},
{
schema: WorkflowDraftWorkspacesSetStepInputBindingsPayloadSchema,
payload: {
...inputPayload,
bindings: [{ target: { root: "local", parts: [] }, value: "title" }],
},
},
{
schema: WorkflowDraftWorkspacesSetStepOutputBindingsPayloadSchema,
payload: {
...outputPayload,
bindings: [{ source: { root: "local", parts: [] }, target: "state.title" }],
},
},
];
for (const testCase of cases) {
expect(accepts(testCase.schema, testCase.payload)).toBe(false);
}
});
it("catalogs every authored RPC exactly once", () => {
const expectedMethods = [
"workflow.health",