Skip to content

Commit 312cee3

Browse files
christsoclaude
andauthored
refactor: rename dataset to suite across codebase (#944)
* refactor: rename dataset to suite across codebase (#943) An eval file is a test suite (lifecycle hooks, workspace setup/teardown, execution config), not a dataset (passive input/output pairs). This renames the `dataset` field to `suite` everywhere: - Core types: EvalTest.suite, EvaluationResult.suite - Wire format: JSONL results write `suite` field - CLI: --group-by suite, --suite flag, trace/trend/pipeline commands - Studio UI: routes /suite/, labels "Suites", API endpoints /suites - OTel: agentv.suite attribute - Example baseline JSONL files updated - Documentation and plugin skill files updated Hard deprecation — no backward-compat aliases for `dataset`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(test): use platform-agnostic paths in pi-coding-agent tests The tests used hardcoded Windows backslash paths which fail on Linux. Use path.join and path.sep for cross-platform compatibility. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add Suites section to eval files documentation Explains the suite concept as requested in #943 comment. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address code review findings from dataset→suite rename - Fix missed verbose log "JSONL Dataset:" → "JSONL Suite:" in jsonl-parser - Clean up stale "legacy" comment in discover.ts - Rename internal datasetFile/datasetFilePath vars in check-eval-baselines script Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a45959a commit 312cee3

105 files changed

Lines changed: 611 additions & 612 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/cli/src/commands/eval/artifact-writer.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export interface AggregateGradingArtifact {
9494
export interface IndexArtifactEntry {
9595
readonly timestamp: string;
9696
readonly test_id: string;
97-
readonly dataset?: string;
97+
readonly suite?: string;
9898
readonly category?: string;
9999
readonly conversation_id?: string;
100100
readonly score: number;
@@ -459,13 +459,13 @@ function safeTestId(testId: string | undefined): string {
459459
return safeArtifactPathSegment(testId, 'unknown');
460460
}
461461

462-
function getDataset(result: EvaluationResult): string | undefined {
463-
return result.dataset;
462+
function getSuite(result: EvaluationResult): string | undefined {
463+
return result.suite;
464464
}
465465

466466
function buildArtifactSubdir(result: EvaluationResult): string {
467467
const segments = [];
468-
const evalSet = getDataset(result);
468+
const evalSet = getSuite(result);
469469
if (evalSet) {
470470
segments.push(safeArtifactPathSegment(evalSet, 'default'));
471471
}
@@ -504,7 +504,7 @@ export function buildIndexArtifactEntry(
504504
return {
505505
timestamp: result.timestamp,
506506
test_id: result.testId ?? 'unknown',
507-
dataset: getDataset(result),
507+
suite: getSuite(result),
508508
category: result.category,
509509
conversation_id: result.conversationId,
510510
score: result.score,
@@ -536,7 +536,7 @@ export function buildResultIndexArtifact(result: EvaluationResult): ResultIndexA
536536
return {
537537
timestamp: result.timestamp,
538538
test_id: result.testId ?? 'unknown',
539-
dataset: getDataset(result),
539+
suite: getSuite(result),
540540
category: result.category,
541541
conversation_id: result.conversationId,
542542
score: result.score,

apps/cli/src/commands/eval/discover.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ export interface DiscoveredEvalFile {
1717
* Discover eval files by glob pattern matching.
1818
*
1919
* Uses `eval_patterns` from `.agentv/config.yaml` if configured,
20-
* otherwise falls back to default patterns that match `dataset*.yaml`
21-
* and `eval.yaml` files under `evals/` directories.
20+
* otherwise falls back to default patterns that match `suite*.yaml`,
21+
* `eval.yaml`, and `dataset*.yaml` files under `evals/` directories.
2222
*/
2323
export async function discoverEvalFiles(cwd: string): Promise<readonly DiscoveredEvalFile[]> {
2424
const repoRoot = await findRepoRoot(cwd);

apps/cli/src/commands/eval/junit-writer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export class JunitWriter {
4747

4848
const grouped = new Map<string, EvaluationResult[]>();
4949
for (const result of this.results) {
50-
const suite = result.dataset ?? 'default';
50+
const suite = result.suite ?? 'default';
5151
const existing = grouped.get(suite);
5252
if (existing) {
5353
existing.push(result);

apps/cli/src/commands/eval/run-eval.ts

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ async function prepareFileMetadata(params: {
480480
readonly testCases: readonly EvalTest[];
481481
readonly selections: readonly { selection: TargetSelection; inlineTargetLabel: string }[];
482482
readonly trialsConfig?: TrialsConfig;
483-
readonly datasetTargets?: readonly string[];
483+
readonly suiteTargets?: readonly string[];
484484
readonly yamlWorkers?: number;
485485
readonly yamlCache?: boolean;
486486
readonly yamlCachePath?: string;
@@ -501,23 +501,23 @@ async function prepareFileMetadata(params: {
501501
const relativePath = path.relative(cwd, testFilePath);
502502
const category = deriveCategory(relativePath);
503503

504-
const dataset = await loadTestSuite(testFilePath, repoRoot, {
504+
const suite = await loadTestSuite(testFilePath, repoRoot, {
505505
verbose: options.verbose,
506506
filter: options.filter,
507507
category,
508508
});
509-
const testIds = dataset.tests.map((value) => value.id);
509+
const testIds = suite.tests.map((value) => value.id);
510510

511511
// Determine target names: CLI --target flags override YAML
512512
const cliTargets = options.cliTargets;
513-
const datasetTargets = dataset.targets;
513+
const suiteTargets = suite.targets;
514514

515-
// Resolve which target names to use (precedence: CLI > dataset YAML targets > default)
515+
// Resolve which target names to use (precedence: CLI > suite YAML targets > default)
516516
let targetNames: readonly string[];
517517
if (cliTargets.length > 0) {
518518
targetNames = cliTargets;
519-
} else if (datasetTargets && datasetTargets.length > 0) {
520-
targetNames = datasetTargets;
519+
} else if (suiteTargets && suiteTargets.length > 0) {
520+
targetNames = suiteTargets;
521521
} else {
522522
targetNames = [];
523523
}
@@ -568,17 +568,17 @@ async function prepareFileMetadata(params: {
568568

569569
return {
570570
testIds,
571-
testCases: dataset.tests,
571+
testCases: suite.tests,
572572
selections,
573-
trialsConfig: dataset.trials,
574-
datasetTargets,
575-
yamlWorkers: dataset.workers,
576-
yamlCache: dataset.cacheConfig?.enabled,
577-
yamlCachePath: dataset.cacheConfig?.cachePath,
578-
totalBudgetUsd: dataset.totalBudgetUsd,
579-
failOnError: dataset.failOnError,
580-
threshold: dataset.threshold,
581-
tags: dataset.metadata?.tags,
573+
trialsConfig: suite.trials,
574+
suiteTargets,
575+
yamlWorkers: suite.workers,
576+
yamlCache: suite.cacheConfig?.enabled,
577+
yamlCachePath: suite.cacheConfig?.cachePath,
578+
totalBudgetUsd: suite.totalBudgetUsd,
579+
failOnError: suite.failOnError,
580+
threshold: suite.threshold,
581+
tags: suite.metadata?.tags,
582582
};
583583
}
584584

@@ -1021,7 +1021,7 @@ export async function runEvalCommand(
10211021
inlineTargetLabel: string;
10221022
}[];
10231023
readonly trialsConfig?: TrialsConfig;
1024-
readonly datasetTargets?: readonly string[];
1024+
readonly suiteTargets?: readonly string[];
10251025
readonly yamlWorkers?: number;
10261026
readonly yamlCache?: boolean;
10271027
readonly yamlCachePath?: string;
@@ -1104,7 +1104,7 @@ export async function runEvalCommand(
11041104
console.log(`Response cache: enabled${yamlCachePath ? ` (${yamlCachePath})` : ''}`);
11051105
}
11061106

1107-
// Resolve dataset-level threshold: CLI --threshold takes precedence over YAML execution.threshold.
1107+
// Resolve suite-level threshold: CLI --threshold takes precedence over YAML execution.threshold.
11081108
const yamlThreshold = firstMeta?.threshold;
11091109
const resolvedThreshold = options.threshold ?? yamlThreshold;
11101110
if (resolvedThreshold !== undefined && (resolvedThreshold < 0 || resolvedThreshold > 1)) {
@@ -1128,13 +1128,13 @@ export async function runEvalCommand(
11281128
// In matrix mode, total eval count is tests × targets (accounting for per-test target overrides)
11291129
let totalEvalCount = 0;
11301130
for (const meta of fileMetadata.values()) {
1131-
const datasetTargetNames = meta.selections.map((s) => s.selection.targetName);
1131+
const suiteTargetNames = meta.selections.map((s) => s.selection.targetName);
11321132
for (const test of meta.testCases) {
1133-
// Per-test targets override dataset-level targets.
1133+
// Per-test targets override suite-level targets.
11341134
const testTargetNames =
11351135
test.targets && test.targets.length > 0
1136-
? test.targets.filter((t) => datasetTargetNames.includes(t))
1137-
: datasetTargetNames;
1136+
? test.targets.filter((t) => suiteTargetNames.includes(t))
1137+
: suiteTargetNames;
11381138
totalEvalCount += testTargetNames.length > 0 ? testTargetNames.length : 1;
11391139
}
11401140
}

apps/cli/src/commands/pipeline/bench.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,15 @@ export const evalBenchCommand = command({
3737
const manifest = JSON.parse(await readFile(join(exportDir, 'manifest.json'), 'utf8'));
3838
const testIds: string[] = manifest.test_ids;
3939
const targetName: string = manifest.target?.name ?? 'unknown';
40-
const datasetName: string = manifest.dataset ?? '';
40+
const suiteName: string = manifest.suite ?? '';
4141
const experiment: string | undefined = manifest.experiment;
42-
const safeDatasetName = datasetName ? datasetName.replace(/[\/\\:*?"<>|]/g, '_') : '';
42+
const safeSuiteName = suiteName ? suiteName.replace(/[\/\\:*?"<>|]/g, '_') : '';
4343

4444
const indexLines: string[] = [];
4545
const allPassRates: number[] = [];
4646

4747
for (const testId of testIds) {
48-
const subpath = safeDatasetName ? [safeDatasetName, testId] : [testId];
48+
const subpath = safeSuiteName ? [safeSuiteName, testId] : [testId];
4949
const testDir = join(exportDir, ...subpath);
5050
const artifactSubdir = subpath.join('/');
5151
const evaluators: EvaluatorScore[] = [];
@@ -177,7 +177,7 @@ export const evalBenchCommand = command({
177177
JSON.stringify({
178178
timestamp: manifest.timestamp,
179179
test_id: testId,
180-
dataset: datasetName || undefined,
180+
suite: suiteName || undefined,
181181
experiment: experiment || undefined,
182182
score: Math.round(weightedScore * 1000) / 1000,
183183
target: targetName,

apps/cli/src/commands/pipeline/grade.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* Progress is printed to stderr so users see real-time feedback.
1111
*
1212
* Export directory additions:
13-
* <out-dir>/<dataset>/<test-id>/code_grader_results/<name>.json
13+
* <out-dir>/<suite>/<test-id>/code_grader_results/<name>.json
1414
*/
1515
import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
1616
import { join } from 'node:path';
@@ -196,14 +196,14 @@ export const evalGradeCommand = command({
196196
const manifestPath = join(exportDir, 'manifest.json');
197197
const manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
198198
const testIds: string[] = manifest.test_ids;
199-
const datasetName: string = manifest.dataset ?? '';
200-
const safeDatasetName = datasetName ? datasetName.replace(/[\/\\:*?"<>|]/g, '_') : '';
199+
const suiteName: string = manifest.suite ?? '';
200+
const safeSuiteName = suiteName ? suiteName.replace(/[\/\\:*?"<>|]/g, '_') : '';
201201

202202
// Collect all grader tasks upfront so we know the total count
203203
const tasks: GraderTask[] = [];
204204

205205
for (const testId of testIds) {
206-
const subpath = safeDatasetName ? [safeDatasetName, testId] : [testId];
206+
const subpath = safeSuiteName ? [safeSuiteName, testId] : [testId];
207207
const testDir = join(exportDir, ...subpath);
208208
const codeGradersDir = join(testDir, 'code_graders');
209209
const resultsDir = join(testDir, 'code_grader_results');

apps/cli/src/commands/pipeline/input.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* Export directory layout:
1010
* <out-dir>/
1111
* ├── manifest.json
12-
* └── <dataset>/ (omitted if eval.yaml has no name)
12+
* └── <suite>/ (omitted if eval.yaml has no name)
1313
* └── <test-id>/
1414
* ├── input.json
1515
* ├── invoke.json
@@ -58,8 +58,8 @@ export const evalInputCommand = command({
5858
const evalDir = dirname(resolvedEvalPath);
5959

6060
const category = deriveCategory(relative(process.cwd(), resolvedEvalPath));
61-
const dataset = await loadTestSuite(resolvedEvalPath, repoRoot, { category });
62-
const tests = dataset.tests;
61+
const suite = await loadTestSuite(resolvedEvalPath, repoRoot, { category });
62+
const tests = suite.tests;
6363

6464
if (tests.length === 0) {
6565
console.error('No tests found in eval file.');
@@ -107,13 +107,13 @@ export const evalInputCommand = command({
107107
// No targets file found — subagent-as-target mode
108108
}
109109

110-
const datasetName = dataset.metadata?.name?.trim() ?? '';
111-
const safeDatasetName = datasetName ? datasetName.replace(/[\/\\:*?"<>|]/g, '_') : '';
110+
const suiteName = suite.metadata?.name?.trim() ?? '';
111+
const safeSuiteName = suiteName ? suiteName.replace(/[\/\\:*?"<>|]/g, '_') : '';
112112

113113
const testIds: string[] = [];
114114

115115
for (const test of tests) {
116-
const subpath = safeDatasetName ? [safeDatasetName, test.id] : [test.id];
116+
const subpath = safeSuiteName ? [safeSuiteName, test.id] : [test.id];
117117
const testDir = join(outDir, ...subpath);
118118
await mkdir(testDir, { recursive: true });
119119
testIds.push(test.id);
@@ -168,7 +168,7 @@ export const evalInputCommand = command({
168168
// manifest.json
169169
await writeJson(join(outDir, 'manifest.json'), {
170170
eval_file: resolvedEvalPath,
171-
dataset: datasetName || undefined,
171+
suite: suiteName || undefined,
172172
experiment: experiment || undefined,
173173
timestamp: new Date().toISOString(),
174174
target: {

apps/cli/src/commands/pipeline/run.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ export const evalRunCommand = command({
100100

101101
// ── Step 1: Extract inputs (same as pipeline input) ──────────────
102102
const category = deriveCategory(relative(process.cwd(), resolvedEvalPath));
103-
const dataset = await loadTestSuite(resolvedEvalPath, repoRoot, { category });
104-
const tests = dataset.tests;
103+
const suite = await loadTestSuite(resolvedEvalPath, repoRoot, { category });
104+
const tests = suite.tests;
105105

106106
if (tests.length === 0) {
107107
console.error('No tests found in eval file.');
@@ -145,13 +145,13 @@ export const evalRunCommand = command({
145145
// No targets file — subagent-as-target mode
146146
}
147147

148-
const datasetName = dataset.metadata?.name?.trim() ?? '';
149-
const safeDatasetName = datasetName ? datasetName.replace(/[\/\\:*?"<>|]/g, '_') : '';
148+
const suiteName = suite.metadata?.name?.trim() ?? '';
149+
const safeSuiteName = suiteName ? suiteName.replace(/[\/\\:*?"<>|]/g, '_') : '';
150150

151151
const testIds: string[] = [];
152152

153153
for (const test of tests) {
154-
const subpath = safeDatasetName ? [safeDatasetName, test.id] : [test.id];
154+
const subpath = safeSuiteName ? [safeSuiteName, test.id] : [test.id];
155155
const testDir = join(outDir, ...subpath);
156156
await mkdir(testDir, { recursive: true });
157157
testIds.push(test.id);
@@ -198,7 +198,7 @@ export const evalRunCommand = command({
198198

199199
await writeJson(join(outDir, 'manifest.json'), {
200200
eval_file: resolvedEvalPath,
201-
dataset: datasetName || undefined,
201+
suite: suiteName || undefined,
202202
experiment: experiment || undefined,
203203
timestamp: new Date().toISOString(),
204204
target: { name: targetName, kind: targetKind },
@@ -230,7 +230,7 @@ export const evalRunCommand = command({
230230
writeInvProgress();
231231

232232
const invokeTarget = async (testId: string): Promise<void> => {
233-
const subpath = safeDatasetName ? [safeDatasetName, testId] : [testId];
233+
const subpath = safeSuiteName ? [safeSuiteName, testId] : [testId];
234234
const testDir = join(outDir, ...subpath);
235235
const invoke = JSON.parse(await readFile(join(testDir, 'invoke.json'), 'utf8'));
236236
if (invoke.kind !== 'cli') return;
@@ -341,7 +341,7 @@ export const evalRunCommand = command({
341341
const graderTasks: GraderTask[] = [];
342342

343343
for (const testId of testIds) {
344-
const subpath = safeDatasetName ? [safeDatasetName, testId] : [testId];
344+
const subpath = safeSuiteName ? [safeSuiteName, testId] : [testId];
345345
const testDir = join(outDir, ...subpath);
346346
const codeGradersDir = join(testDir, 'code_graders');
347347
const resultsDir = join(testDir, 'code_grader_results');

apps/cli/src/commands/results/manifest.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
export interface ResultManifestRecord {
1414
readonly timestamp?: string;
1515
readonly test_id?: string;
16-
readonly dataset?: string;
16+
readonly suite?: string;
1717
readonly category?: string;
1818
readonly experiment?: string;
1919
readonly target?: string;
@@ -123,7 +123,7 @@ function hydrateManifestRecord(baseDir: string, record: ResultManifestRecord): E
123123
return {
124124
timestamp: record.timestamp,
125125
testId,
126-
dataset: record.dataset,
126+
suite: record.suite,
127127
category: record.category,
128128
target: record.target,
129129
score: record.score,
@@ -189,6 +189,7 @@ export function loadManifestResults(sourceFile: string): EvaluationResult[] {
189189

190190
export interface LightweightResultRecord {
191191
readonly testId: string;
192+
readonly suite?: string;
192193
readonly target?: string;
193194
readonly experiment?: string;
194195
readonly score: number;
@@ -203,6 +204,7 @@ export function loadLightweightResults(sourceFile: string): LightweightResultRec
203204
const content = readFileSync(resolvedSourceFile, 'utf8');
204205
return parseResultManifest(content).map((record) => ({
205206
testId: record.test_id ?? 'unknown',
207+
suite: record.suite,
206208
target: record.target,
207209
experiment: record.experiment,
208210
score: record.score,

0 commit comments

Comments
 (0)