Fix canonical Cursor plan delivery expectations

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-09-29 17:16:53 -05:00
1 parent 1d5196c8e8
commit 0ccdd4f03f
2 files changed
+29 -1

No files matched your search

@@ -1,3 +1,6 @@
import { parsePaperclipQuestionResponse } from "../../packages/paperclip-runner/src/contracts/question-set.js";
import { normalizeCursorPlanRequest } from "../../packages/paperclip-runner/src/drivers/acpx/cursor-extensions.js";
import { cursorNativePlanResponse } from "./cursor-native-flow.js";
import { cursorDeniedCommand, type CursorToolNotice } from "./cursor-native-evidence.js";
import { describe, expect, it } from "vitest";
import { cursorNativeCaseDesigns, cursorNativePrompt, hasDeliveredCursorNativeRequest, hasCursorPlanDecision, hasCursorDenialBoundary, CURSOR_DENIAL_SAMPLE_PHASES, hasExactCursorNativeResponse } from "./cursor-native-cases.js";
@@ -62,3 +65,23 @@ it("requires exact delivered answer bytes rather than a saved or different answe
expect(hasExactCursorNativeResponse({ ...input, response: { ...response, answers: { q: { selectedOptionIds: ["other"] } } } })).toBe(false);
expect(hasExactCursorNativeResponse({ ...input, events: rows.slice(0, 1) })).toBe(false);
});
it.each(["accept", "cancel", "reject"] as const)("recognizes canonical delivered native plan %s without an empty optional answer", decision => {
const native = normalizeCursorPlanRequest({ toolCallId: "native-plan", plan: "Verify the revised plan.", todos: [] });
const planId = native.questionSet.questions[0]!.id;
const response = cursorNativePlanResponse(planId, decision, " Keep exact feedback 漢字\nSecond line ");
const delivered = parsePaperclipQuestionResponse(native.questionSet, response);
const rows = proof();
(rows[0]!.payload.prpEvent.payload as any).request.origin.method = "cursor/create_plan";
(rows[1]!.payload.prpEvent.payload as any).response = delivered;
const input = { events: rows, runId: "run", turnId: "turn", requestId: "request", method: "cursor/create_plan" as const, action: "submit" as const, response };
expect(hasCursorPlanDecision(native.questionSet, response, decision)).toBe(true);
expect(hasExactCursorNativeResponse(input)).toBe(true);
expect(native.resolve(delivered).outcome.outcome).toBe({ accept: "accepted", cancel: "cancelled", reject: "rejected" }[decision]);
if (decision === "reject") expect(delivered.answers.reason?.text).toBe(" Keep exact feedback 漢字\nSecond line ");
else {
expect(delivered.answers).not.toHaveProperty("reason");
expect(hasExactCursorNativeResponse({ ...input, response: { ...response, answers: { ...response.answers, reason: {} } } })).toBe(false);
}
});
+6 -1
View File
@@ -14,6 +14,11 @@ import type { MatrixExecution } from "./types.js";
type Row = Record<string, any>;
type Check = { id: string; passed: boolean; detail: string };
/** Match canonical omission of an unanswered optional feedback field. */
export function cursorNativePlanResponse(planId: string, decision: "accept" | "reject" | "cancel", feedback: string) {
return { schema: "paperclip.question_response.v1", answers: { [planId]: { selectedOptionIds: [decision] }, ...(decision === "reject" ? { reason: { text: feedback } } : {}) } };
}
/** Independent bounded snapshot; symlinks are rejected without following them. */
export async function cursorNativeWorkspaceSnapshot(root: string): Promise<Record<string, string>> {
const files: Record<string, string> = {}; let bytes = 0;
@@ -183,7 +188,7 @@ export async function runCursorNativeFlow(input: {
await page.getByRole("radio", { name: decision === "accept" ? "Accept plan" : decision === "reject" ? "Reject plan" : "Cancel plan request", exact: true }).last().click();
await page.getByRole("button", { name: "Next", exact: true }).last().click();
if (decision === "reject") await page.getByTestId("question-text-answer-composer").last().locator('[contenteditable="true"],textarea').first().fill(feedback);
const response = { schema: "paperclip.question_response.v1", answers: { [plan.id]: { selectedOptionIds: [decision] }, reason: decision === "reject" ? { text: feedback } : {} } };
const response = cursorNativePlanResponse(plan.id, decision, feedback);
check("revision-bound-decision", hasCursorPlanDecision(set, response, decision), "Decision addresses precisely the displayed native plan revision");
await page.getByRole("button", { name: set.submitLabel ?? "Submit answers", exact: true }).last().click();
await delivery(card, event, response); seen.add(card.id); previousRevision = plan.id;