Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/benchmarks/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export interface BenchmarkShowInput {
project?: string;
workload?: string;
includeReport?: boolean;
expectPublication?: string;
}

export interface BenchmarkShowResult {
Expand Down Expand Up @@ -120,6 +121,9 @@ export function createBenchmarksService(options: ManagementScopeOptions): Benchm
},

async show(input) {
if (input.expectPublication !== undefined && input.expectPublication.length === 0) {
throw new CliError("Expected publication id must not be empty.");
}
const scope = await resolveManagementScope(options, {
project: input.project,
workload: input.workload,
Expand All @@ -136,6 +140,9 @@ export function createBenchmarksService(options: ManagementScopeOptions): Benchm
(result.publication && (result.publication.org_id !== session.organizationId || result.publication.project_id !== project!.id || result.publication.workload_id !== workload!.id))) {
throw new CliError("Understudy returned a benchmark outside the selected workload.");
}
if (input.expectPublication !== undefined && result.publication?.id !== input.expectPublication) {
throw new CliError("The published benchmark has changed. Refresh the dashboard and copy the agent handoff again.");
}
const report = result.benchmark?.report;
return {
project: { id: project!.id, slug: project!.slug, name: project!.name },
Expand Down
3 changes: 2 additions & 1 deletion src/commands/benchmarks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ export function addBenchmarksCommands(
.option("--project <selector>", "Exact project id, slug, or name; defaults to saved context.")
.option("--workload <selector>", "Exact workload id or name; defaults to saved context.")
.option("--include-report", "Include the full benchmark manifest, which may contain customer prompt and eval content.")
.action(async function (this: Command, options: { project?: string; workload?: string; includeReport?: boolean }) {
.option("--expect-publication <id>", "Require this exact publication; fail if the current benchmark changed.")
.action(async function (this: Command, options: { project?: string; workload?: string; includeReport?: boolean; expectPublication?: string }) {
const result = await service.show(options);
output(formatBenchmark(this, result, options.includeReport === true));
});
Expand Down
6 changes: 6 additions & 0 deletions tests/benchmarks-command.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ test("benchmarks show gives an agent the full JSON report only when requested",
assert.equal(JSON.parse(output[0]).benchmark.report.prompt_change.diff[0], "Invented prompt line");
});

test("benchmarks show forwards the publication pin", async () => {
const { program, calls } = setup();
await program.parseAsync(["node", "understudy", "benchmarks", "show", "--expect-publication", "synthetic-publication"]);
assert.deepEqual(calls, [{ expectPublication: "synthetic-publication" }]);
});

test("benchmarks list prints available publications", async () => {
const { program, output } = setup();
await program.parseAsync(["node", "understudy", "benchmarks", "list"]);
Expand Down
19 changes: 19 additions & 0 deletions tests/benchmarks-service.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,25 @@ test("benchmark detail exposes an explicit empty state", async () => {
assert.equal(result.manifestBytes, null);
});

test("benchmark detail refuses a replaced or missing publication when pinned", async () => {
const service = fixture(async (input) => responseFor(input));
const current = await service.show({ project: projectId, workload: workloadId, expectPublication: publication.id });
assert.equal(current.publication.id, publication.id);
await assert.rejects(
service.show({ project: projectId, workload: workloadId, expectPublication: "synthetic-older-publication", includeReport: true }),
/published benchmark has changed/,
);
await assert.rejects(
service.show({ project: projectId, workload: workloadId, expectPublication: "" }),
/Expected publication id must not be empty/,
);
await assert.rejects(
fixture(async (input) => responseFor(input, { benchmark: null, publication: null }))
.show({ project: projectId, workload: workloadId, expectPublication: publication.id }),
/published benchmark has changed/,
);
});

test("benchmark detail rejects another workload's response", async () => {
await assert.rejects(
fixture(async (input) => responseFor(input, {
Expand Down
Loading