1import { assert } from "@effect/vitest";
2import type { ProviderReplayTranscript } from "@t3tools/contracts";
3
4import type { OrchestratorV2ScenarioResult } from "../../OrchestratorScenario.ts";
5import {
6 assertBaseProjection,
7 assertRunOrdinals,
8 assertSemanticProjectionIntegrity,
9 assertTurnItemTypeSequence,
10 assertVisibleTurnItemTypeSequence,
11 assertVisibleUserMessagesExclude,
12 assertVisibleUserMessagesInclude,
13 assertUserMessagesInclude,
14 projectionFor,
15 THREAD_ROLLBACK_AFTER_PROMPT,
16 THREAD_ROLLBACK_FIRST_PROMPT,
17 THREAD_ROLLBACK_SECOND_PROMPT,
18} from "../shared.ts";
19
20export function assertThreadRollbackOutput(
21 result: OrchestratorV2ScenarioResult,
22 transcript: ProviderReplayTranscript,
23) {
24 assertBaseProjection({
25 result,
26 transcript,
27 runCount: 3,
28 runStatuses: ["completed", "rolled_back", "completed"],
29 });
30
31 const projection = projectionFor(result, transcript.scenario);
32 assertSemanticProjectionIntegrity(projection);
33 assertRunOrdinals(projection, [1, 2, 3]);
34 assertTurnItemTypeSequence(projection, [
35 "user_message",
36 "assistant_message",
37 "checkpoint",
38 "user_message",
39 "assistant_message",
40 "checkpoint",
41 "user_message",
42 "assistant_message",
43 "checkpoint",
44 ]);
45 assertVisibleTurnItemTypeSequence(projection, [
46 "user_message",
47 "assistant_message",
48 "checkpoint",
49 "user_message",
50 "assistant_message",
51 "checkpoint",
52 ]);
53 assertUserMessagesInclude(projection, [
54 THREAD_ROLLBACK_FIRST_PROMPT,
55 THREAD_ROLLBACK_SECOND_PROMPT,
56 THREAD_ROLLBACK_AFTER_PROMPT,
57 ]);
58 assertVisibleUserMessagesInclude(projection, [
59 THREAD_ROLLBACK_FIRST_PROMPT,
60 THREAD_ROLLBACK_AFTER_PROMPT,
61 ]);
62 assertVisibleUserMessagesExclude(projection, [THREAD_ROLLBACK_SECOND_PROMPT]);
63 // Codex's own context was rewound: the post-rollback recall omits the reverted turn.
64 const recall = projection.turnItems.findLast((item) => item.type === "assistant_message");
65 assert.include(recall?.type === "assistant_message" ? recall.text : "", "first turn complete");
66 assert.notInclude(recall?.type === "assistant_message" ? recall.text : "", "second turn");
67 assert.isAtLeast(projection.checkpoints.length, 2);
68 assert.isTrue(
69 projection.runs.some((run) => run.status === "rolled_back"),
70 "rollback must be visible in run state",
71 );
72}