Skip to content

Commit 0858ef2

Browse files
sagiwclaude
andcommitted
test(interactions): cover ask_user_questions payload normalization
Verifies the intuitive string-option / omitted-selectionMode shape now parses and normalizes, the canonical object shape is unchanged, and duplicate option ids are still rejected after normalization. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 1cc2f94 commit 0858ef2

1 file changed

Lines changed: 69 additions & 0 deletions

File tree

packages/shared/src/validators/issue.test.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
22
import { MAX_ISSUE_REQUEST_DEPTH } from "../index.js";
33
import {
44
addIssueCommentSchema,
5+
askUserQuestionsPayloadSchema,
56
createIssueSchema,
67
issueBlockedInboxAttentionSchema,
78
resolveIssueRecoveryActionSchema,
@@ -541,3 +542,71 @@ describe("issue validators", () => {
541542
expect(parsed.success).toBe(false);
542543
});
543544
});
545+
546+
describe("askUserQuestionsPayloadSchema", () => {
547+
it("normalizes the intuitive string-option / no-selectionMode shape agents send", () => {
548+
// The exact shape that previously returned 400 and never reached the user:
549+
// options as plain strings, selectionMode omitted (Claude's own tool shape).
550+
const parsed = askUserQuestionsPayloadSchema.safeParse({
551+
version: 1,
552+
questions: [
553+
{
554+
id: "deploy-approval",
555+
prompt: "Approve the staging deploy?",
556+
options: ["Yes, ship it", "No, hold"],
557+
},
558+
],
559+
});
560+
561+
expect(parsed.success).toBe(true);
562+
if (!parsed.success) return;
563+
const question = parsed.data.questions[0]!;
564+
expect(question.selectionMode).toBe("single");
565+
expect(question.options).toEqual([
566+
{ id: "yes-ship-it", label: "Yes, ship it" },
567+
{ id: "no-hold", label: "No, hold" },
568+
]);
569+
});
570+
571+
it("accepts the canonical object-shaped payload unchanged", () => {
572+
const parsed = askUserQuestionsPayloadSchema.safeParse({
573+
version: 1,
574+
questions: [
575+
{
576+
id: "q1",
577+
prompt: "Pick one",
578+
selectionMode: "multi",
579+
options: [
580+
{ id: "a", label: "Option A" },
581+
{ id: "b", label: "Option B" },
582+
],
583+
},
584+
],
585+
});
586+
587+
expect(parsed.success).toBe(true);
588+
if (!parsed.success) return;
589+
const question = parsed.data.questions[0]!;
590+
expect(question.selectionMode).toBe("multi");
591+
expect(question.options).toEqual([
592+
{ id: "a", label: "Option A" },
593+
{ id: "b", label: "Option B" },
594+
]);
595+
});
596+
597+
it("still rejects duplicate option ids after normalization", () => {
598+
const parsed = askUserQuestionsPayloadSchema.safeParse({
599+
version: 1,
600+
questions: [
601+
{
602+
id: "q1",
603+
prompt: "Pick one",
604+
// Both string options slugify to the same id.
605+
options: ["Ship it", "Ship it"],
606+
},
607+
],
608+
});
609+
610+
expect(parsed.success).toBe(false);
611+
});
612+
});

0 commit comments

Comments
 (0)