Summary
Keep agentv's documented architecture — camelCase TypeScript internals, snake_case persisted wire (YAML/JSONL) — but eliminate duplication and edge-case drift by consolidating the conversion layer into a single, well-tested, Zod-backed boundary serializer.
Decision reversal: NOT snake_case-native
An earlier idea was to drop the converter and make types snake_case-native (the OpenAI / Stripe / Anthropic-SDK pattern). That pattern fits single-provider SDKs whose TS types are their HTTP API surface (Stainless code-gens snake_case interface keys; type-safety is compile-time only, no runtime conversion).
agentv is different: it's a multi-provider abstraction (claude, codex, copilot, pi, vscode, agentv, cli, function, replay, mock) that normalizes many provider shapes into one canonical internal model. That's the Vercel AI SDK case, where a canonical camelCase internal model + conversion at the wire is the correct architecture. The snake_case wire exists for Python-ecosystem / JSONL portability. The split is already a documented contract (packages/core/src/evaluation/trace.ts:15-17), and Zod is already in use.
Actual problem: 4 duplicated converters that can drift
packages/sdk/src/case-conversion.ts
packages/core/src/evaluation/case-conversion.ts
apps/cli/src/utils/case-conversion.ts
- inline copy in
packages/core/src/evaluation/run-artifacts.ts (~line 1818)
Each can diverge on edge cases: acronyms (HTTPStatus), numeric boundaries (top_p↔topP), and the uppercase proper-noun tool-name exception (Read, Edit).
Goal
- One shared, exhaustively-tested converter (single source of truth) imported by SDK, core, and CLI; delete the duplicates and the inline
run-artifacts copy.
- Zod-backed wire boundary: canonical camelCase schemas + a typed snake transform on serialize/parse, so conversion is validated rather than a stringly-typed deep walk.
- Preserve byte-compatible JSONL/artifact output and the camelCase-internal / snake-wire contract.
Non-goals
- Changing grader-author-facing field casing (stays camelCase internal view).
- Removing the conversion boundary (it's required for multi-provider normalization + Python wire).
Tracking
Beads epic av-wwu5 (children av-wwu5.1 ADR/inventory → .2 SDK → .3 Core+Zod → .4 CLI → .5 validation).
Summary
Keep agentv's documented architecture — camelCase TypeScript internals, snake_case persisted wire (YAML/JSONL) — but eliminate duplication and edge-case drift by consolidating the conversion layer into a single, well-tested, Zod-backed boundary serializer.
Decision reversal: NOT snake_case-native
An earlier idea was to drop the converter and make types snake_case-native (the OpenAI / Stripe / Anthropic-SDK pattern). That pattern fits single-provider SDKs whose TS types are their HTTP API surface (Stainless code-gens snake_case interface keys; type-safety is compile-time only, no runtime conversion).
agentv is different: it's a multi-provider abstraction (claude, codex, copilot, pi, vscode, agentv, cli, function, replay, mock) that normalizes many provider shapes into one canonical internal model. That's the Vercel AI SDK case, where a canonical camelCase internal model + conversion at the wire is the correct architecture. The snake_case wire exists for Python-ecosystem / JSONL portability. The split is already a documented contract (
packages/core/src/evaluation/trace.ts:15-17), and Zod is already in use.Actual problem: 4 duplicated converters that can drift
packages/sdk/src/case-conversion.tspackages/core/src/evaluation/case-conversion.tsapps/cli/src/utils/case-conversion.tspackages/core/src/evaluation/run-artifacts.ts(~line 1818)Each can diverge on edge cases: acronyms (
HTTPStatus), numeric boundaries (top_p↔topP), and the uppercase proper-noun tool-name exception (Read,Edit).Goal
run-artifactscopy.Non-goals
Tracking
Beads epic av-wwu5 (children av-wwu5.1 ADR/inventory → .2 SDK → .3 Core+Zod → .4 CLI → .5 validation).