import { describe, expect, it } from "vitest";
import { renderCaseOutcome } from "./case-outcome.js";
import { renderRunnerE2EDashboard } from "./dashboard.js";
import {
firstTaskTranscript,
firstTaskUserRequest,
renderFirstTaskTranscript,
} from "./first-task-transcript.js";
import type {
FirstTaskEvidence,
FirstTaskCheckpoint,
} from "./first-task-scoring.js";
import type { RunnerE2EResult } from "./types.js";
function recording(): FirstTaskEvidence {
const opening: FirstTaskCheckpoint = {
id: "opening",
phase: "opening",
at: "2026-09-15T00:00:00Z",
issueId: "task",
tasks: [],
agents: [{ id: "agent", name: "Alex" }],
runs: [],
documents: [],
comments: [{ id: "greeting", authorAgentId: "agent", body: "Welcome" }],
interactions: [
{
id: "question",
kind: "ask_user_questions",
status: "pending",
payload: {
questions: [
{
id: "q",
prompt: "What would help?",
options: [{ id: "plan", label: "Make a plan" }],
},
],
},
},
],
};
const response = structuredClone(opening);
Object.assign(response, {
id: "response",
phase: "response",
at: "2026-09-15T00:01:00Z",
});
Object.assign(response.interactions[0], {
status: "answered",
resolvedAt: "2026-09-15T00:00:20Z",
result: {
answers: [
{
questionId: "q",
optionIds: ["plan"],
otherText: "Use our garden club facts",
},
],
},
});
response.comments.push({
id: "reply",
authorAgentId: "agent",
body: "Shall I proceed? ",
createdAt: "2026-09-15T00:00:50Z",
});
response.documents.push({
id: "doc",
key: "plan",
title: "Proposal",
body: "Original scope",
latestRevisionId: "v1",
latestRevisionNumber: 1,
});
const finished = structuredClone(response);
Object.assign(finished, {
id: "finished",
phase: "finished",
at: "2026-09-15T00:02:00Z",
});
Object.assign(finished.documents[0], {
body: "Revised scope",
latestRevisionId: "v2",
latestRevisionNumber: 2,
});
return {
caseId: "clear-task-first-response",
nonce: "test",
onboardingIssueId: "task",
agentId: "agent",
initialTaskIds: ["task"],
instructions: [],
configuredModel: null,
observedModels: [],
checkpoints: [opening, response, finished],
checks: [
{
id: "no-premature-work",
passed: true,
detail: "No work before approval",
evidence: ["response"],
},
],
};
}
function result(): RunnerE2EResult {
return {
schema: "paperclip.runner-e2e.result/v2",
suiteId: "first-task",
executionId: "first-task.legacy-codex.local.clear-task-first-response",
attempt: 1,
status: "failed",
profileId: "legacy-codex",
environmentId: "local",
caseId: "clear-task-first-response",
provider: "openai",
model: "unknown",
runtimeMode: "legacy",
startedAt: "2026-09-15T00:00:00Z",
finishedAt: "2026-09-15T00:02:00Z",
durationMs: 120000,
runIds: [],
cleanup: "passed",
firstTask: recording(),
failureClass: "secret_leak",
error:
"Credential detected in persisted Paperclip home: sessions/example.json",
};
}
describe("first-task conversation report", () => {
it("grounds requirements in submitted user comments and resolved form answers", () => {
const e = recording();
for (const checkpoint of e.checkpoints) {
for (const row of [...checkpoint.comments, ...checkpoint.interactions]) row.issueId = "task";
Object.assign(checkpoint.interactions[0], { resolvedByUserId: "board" });
}
e.checkpoints.at(-1)!.comments.push({ id: "user-scope", issueId: "task", authorUserId: "board", body: "Invite beginners explicitly." });
const request = firstTaskUserRequest(e);
expect(request).toContain("[comment:task:user-scope] Invite beginners explicitly.");
expect(request).toContain("[answer:question] Make a plan\nUse our garden club facts");
expect(request.match(/Use our garden club facts/g)).toHaveLength(1);
expect(request).not.toMatch(/Shall I proceed|Welcome|GARDENtest|two-sentence/);
});
it("does not infer requirements from scenario defaults, agent answers, or unrelated tasks", () => {
const e = recording();
for (const checkpoint of e.checkpoints) {
for (const row of checkpoint.interactions) Object.assign(row, { issueId: "task", resolvedByAgentId: "agent" });
checkpoint.comments.push({ id: "foreign", issueId: "other-task", authorUserId: "board", body: "Foreign requirement" });
}
expect(() => firstTaskUserRequest(e)).toThrow("Missing recorded user request");
});
it("deduplicates observations, keeps first-observed time, and includes answers and each document revision", () => {
const entries = firstTaskTranscript(recording());
expect(entries.filter((e) => e.kind === "comment")).toHaveLength(2);
expect(entries.filter((e) => e.kind === "interaction")).toHaveLength(1);
expect(entries.filter((e) => e.kind === "answer")).toHaveLength(1);
expect(
entries.filter((e) => e.kind === "document").map((e) => e.row.body),
).toEqual(["Original scope", "Revised scope"]);
expect(entries.find((e) => e.row.id === "greeting")?.at).toBe(
"2026-09-15T00:00:00Z",
);
expect(entries.find((e) => e.kind === "interaction")?.row.status).toBe(
"answered",
);
expect(entries.findIndex((e) => e.kind === "answer")).toBeLessThan(
entries.findIndex((e) => e.row.id === "reply"),
);
});
it("renders full text, option labels, source references, and escaped untrusted chat", () => {
const e = recording();
const longText = "Full retained body ".repeat(1000);
e.checkpoints.at(-1)!.comments.push({ id: "long", body: longText });
const rendered = renderFirstTaskTranscript(e, (id) => `#${id}`);
expect(rendered).toContain(longText);
expect(rendered).toContain("Agent · Alex");
expect(rendered).toContain("transcript-comment transcript-agent");
expect(rendered).toContain("transcript-comment transcript-human");
expect(rendered).toContain("transcript-answer transcript-human");
expect(rendered).toContain("transcript-interaction transcript-card");
expect(rendered).toContain("User card response");
expect(rendered).toContain("Make a plan\nUse our garden club facts");
expect(rendered).toContain('href="#finished"');
expect(rendered).toContain("<script>unsafe()</script>");
expect(rendered).not.toContain("