1import { assert } from "@effect/vitest";
2import type { ProviderReplayTranscript } from "@t3tools/contracts";
3
4import type { OrchestratorV2ScenarioResult } from "../../OrchestratorScenario.ts";
5import {
6 assertBaseProjection,
7 assertExecutionNodeKinds,
8 assertRunOrdinals,
9 assertSemanticProjectionIntegrity,
10 assertTurnItemTypes,
11 assertUserMessagesInclude,
12 assertVisibleTurnItemsMirrorLocalTurnItems,
13 projectionFor,
14 PROPOSED_PLAN_PROMPT,
15} from "../shared.ts";
16
17export function assertProposedPlanOutput(
18 result: OrchestratorV2ScenarioResult,
19 transcript: ProviderReplayTranscript,
20) {
21 assertBaseProjection({ result, transcript, runCount: 1, runStatuses: ["completed"] });
22
23 const projection = projectionFor(result, transcript.scenario);
24 assertSemanticProjectionIntegrity(projection);
25 assertVisibleTurnItemsMirrorLocalTurnItems(projection);
26 assertRunOrdinals(projection, [1]);
27 assertExecutionNodeKinds(projection, ["root_turn", "plan"]);
28 assertTurnItemTypes(projection, ["user_message", "proposed_plan"]);
29 assertUserMessagesInclude(projection, [PROPOSED_PLAN_PROMPT]);
30
31 const proposedPlans = projection.plans.filter((plan) => plan.kind === "proposed_plan");
32 assert.isAtLeast(proposedPlans.length, 1);
33 // The plan is the model's own; check it answers the prompt rather than its title.
34 assert.match(proposedPlans.at(-1)?.markdown ?? "", /replay/iu);
35 assert.match(proposedPlans.at(-1)?.markdown ?? "", /fixture/iu);
36 // A finished proposal stays active until Implement consumes it; clients only
37 // offer Implement for an active plan.
38 assert.equal(proposedPlans.at(-1)?.status, "active");
39
40 const proposedPlanItems = projection.turnItems.filter((item) => item.type === "proposed_plan");
41 assert.isAtLeast(proposedPlanItems.length, 1);
42 assert.equal(proposedPlanItems.at(-1)?.streaming, false);
43}