From 766369c68fbb5424134f93ade88443b42ff2de44 Mon Sep 17 00:00:00 2001 From: Dotta Date: Tue, 22 Sep 2026 18:34:23 -0700 Subject: [PATCH] test(grok): reject reasoning projected as task replies Co-Authored-By: Paperclip --- tests/runner-e2e/run-observations.ts | 13 +++++++++++++ tests/runner-e2e/runner.spec.ts | 5 ++++- tests/runner-e2e/support.test.ts | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/tests/runner-e2e/run-observations.ts b/tests/runner-e2e/run-observations.ts index e0f18d49cef..429cb87ec0f 100644 --- a/tests/runner-e2e/run-observations.ts +++ b/tests/runner-e2e/run-observations.ts @@ -81,6 +81,19 @@ function record(value: unknown): Record { : {}; } +export function reasoningProjectionFailures(events: readonly ObservableRunEvent[]): string[] { + const misclassified = events.filter((event) => { + const envelope = record(record(event.payload).prpEvent); + const payload = record(envelope.payload); + return envelope.eventType === "item.delta" && + payload.kind === "agentMessage" && + record(payload.update).kind === "reasoning"; + }); + return misclassified.length === 0 ? [] : [ + `provider reasoning was projected as assistant text in ${misclassified.length} durable events`, + ]; +} + export function isNonExecutingReviewFenceRun(run: ObservableRunState) { return ( run.status === "cancelled" && diff --git a/tests/runner-e2e/runner.spec.ts b/tests/runner-e2e/runner.spec.ts index 9649bf1da52..39ad9f14272 100644 --- a/tests/runner-e2e/runner.spec.ts +++ b/tests/runner-e2e/runner.spec.ts @@ -27,6 +27,7 @@ import { isOpenRouterDeepSeekHelloTerminalVariance, numberedPlanStepCount, providerSessionContinuityFailures, + reasoningProjectionFailures, } from "./run-observations.js"; import { resolveRunnerE2ESource } from "./source.js"; import { isValidNativePrpEnvelope } from "./native-event-envelope.js"; @@ -1732,7 +1733,9 @@ for (const execution of executions) { const pendingInteractions = terminal.interactions.filter( (interaction) => interaction.status === "pending", ); - const invariantFailures: string[] = []; + const invariantFailures: string[] = reasoningProjectionFailures( + runEventsByRun.flatMap((entry) => entry.events), + ); if (pendingInteractions.length > 0) invariantFailures.push( `expected no unresolved interaction; observed ${pendingInteractions.length}`, diff --git a/tests/runner-e2e/support.test.ts b/tests/runner-e2e/support.test.ts index 855efccaf35..87c4a4ae2da 100644 --- a/tests/runner-e2e/support.test.ts +++ b/tests/runner-e2e/support.test.ts @@ -44,10 +44,30 @@ import { isOpenRouterDeepSeekHelloTerminalVariance, numberedPlanStepCount, providerSessionContinuityFailures, + reasoningProjectionFailures, } from "./run-observations.js"; import { runnerE2EWebServerCommand } from "./web-server-command.js"; const cleanupDirectories: string[] = []; + +it("rejects reasoning mislabeled as assistant text without echoing private content", () => { + const reasoning = { + eventType: "item.delta", + payload: { prpEvent: { + eventType: "item.delta", + payload: { kind: "agentMessage", text: "PRIVATE_THOUGHT", update: { kind: "reasoning" } }, + } }, + }; + expect(reasoningProjectionFailures([reasoning])).toEqual([ + "provider reasoning was projected as assistant text in 1 durable events", + ]); + const correctlyTyped = structuredClone(reasoning); + correctlyTyped.payload.prpEvent.payload.kind = "reasoning"; + expect(reasoningProjectionFailures([correctlyTyped])).toEqual([]); + const assistant = structuredClone(reasoning); + assistant.payload.prpEvent.payload.update.kind = "agentMessage"; + expect(reasoningProjectionFailures([assistant])).toEqual([]); +}); afterEach(async () => { vi.unstubAllEnvs(); vi.unstubAllGlobals();