Skip to content

test: add agent evals#229

Open
angeloashmore wants to merge 27 commits into
mainfrom
aa/evals
Open

test: add agent evals#229
angeloashmore wants to merge 27 commits into
mainfrom
aa/evals

Conversation

@angeloashmore

@angeloashmore angeloashmore commented Jul 21, 2026

Copy link
Copy Markdown
Member

Resolves:

Description

Adds an eval suite that measures how well AI agents (like Claude Code) use the CLI.

Each eval gives an agent a task in a temp project. The agent runs the real CLI against a real repo, and the test checks the outcome. A few notes on the design:

  • Evals are plain Vitest tests. They reuse the existing e2e fixtures and grade results with normal asserts, plus an LLM judge for criteria that need judgement (for example, "are these field types sensible?").
  • Each eval runs several trials. We track pass rates per run instead of a single pass/fail, since agent behavior varies.
  • Evals are measurements, not gates. Evals the agent fails today stay enabled and score 0% until the CLI improves; an eval is only skipped when the CLI cannot pass it (missing feature).
  • Each eval gets its own throwaway repository, so evals run concurrently without interfering and agents are free to push. Global teardown sweeps repositories created by agents that the harness cannot name in advance (e.g. prismic init).
  • Results append to a JSONL history tagged with the CLI commit, so runs can be compared across CLI versions with node --run evals:report (scope with EVAL_HISTORY).
  • Evals are opt-in (node --run evals) and refuse to run outside a container or CI, because the agent runs with permissions bypassed.
  • A full run is 32 evals × 3 trials: roughly $16 and 15 minutes.

Checklist

  • A comprehensive Linear ticket, providing sufficient context and details to facilitate the review of the PR, is linked to the PR.
  • If my changes require tests, I added them.
  • If my changes affect backward compatibility, it has been discussed.
  • If my changes require an update to the CONTRIBUTING.md guide, I updated it.

Preview

node --run evals runs the suite as normal Vitest tests; a custom reporter prints pass rates and cost at the end (excerpt):

 ✓ evals/edit-models-precisely.eval.ts > adds a field without disturbing existing fields
 ✗ evals/edit-models-precisely.eval.ts > renames a field without disturbing field order
 …

Pass rates:
  ✓ adds a rich text field to a type: 3/3 passed (100%), $0.30
  ✓ models a testimonial slice from a text description: 1/1 passed (100%), $0.13
  ✓ adds a field without disturbing existing fields: 1/1 passed (100%), $0.11
  ✗ renames a field without disturbing field order: 0/1 passed (0%), $0.24
  …

node --run evals:report digests the JSONL history: one block per eval with one line per run, so scores compare across CLI commits (excerpt):

adds a rich text field to a type
  Jul 21 03:25 @ f235c35   3/3 (100%)   $0.27   205k tok   7.0 turns   16s
  Jul 21 19:34 @ 2f3a7a0   3/3 (100%)   $0.30   235k tok   8.0 turns   19s

writes a slice component that follows Prismic conventions
  Jul 21 19:34 @ 2f3a7a0   0/1   (0%)   $0.38   703k tok   25.0 turns   87s
  Jul 21 19:41 @ 2f3a7a0   1/1 (100%)   $0.47   936k tok   22.0 turns   102s

…

run Jul 21 19:34 @ 2f3a7a0: 29/35 trials passed (83%) across 33 evals · $6.26

How to QA 1

Inside a disposable container with .env.test.local available:

PRISMIC_ALLOW_EVALS=true node --run evals
node --run evals:report

🤖 Generated with Claude Code


Note

Medium Risk
Evals run a real agent with permission bypass against live Prismic accounts and bill Anthropic API usage; mitigated by opt-in PRISMIC_ALLOW_EVALS and isolated-repo design, but misuse outside a disposable environment is dangerous.

Overview
Introduces an agent eval suite (node --run evals) that measures how well an AI agent uses the Prismic CLI on realistic tasks in disposable temp projects.

The harness in evals/it.ts drives the Claude Agent SDK with bypassed permissions, a pinned prismicio/skills system prompt, a prismic CLI shim, and custom matchers (toHaveRun, toSatisfyJudge via Haiku). Trials repeat per eval (EVAL_TRIALS); failures are tracked as pass-rate signals, not CI gates.

Eval modules cover safety (docs before acting, no destructive ambiguity), repo setup (previews, tokens, webhooks, locales), routes, content modeling, precise edits, init/sync, and React slice wiring—some cases are it.skip or annotated when the CLI cannot pass yet.

Reporting: evals/reporter.ts appends per-trial rows (tokens, cost, CLI commit) to evals/results.jsonl; evals/report.ts summarizes history across runs. Git attributes treat results.jsonl as union-merge; .env.test.example documents ANTHROPIC_API_KEY.

Reviewed by Cursor Bugbot for commit bee3782. Bugbot is set up for automated code reviews on this repo. Configure here.

Footnotes

  1. Please use these labels when submitting a review:
    ❓ #ask: Ask a question.
    💡 #idea: Suggest an idea.
    ⚠️ #issue: Strongly suggest a change.
    🎉 #nice: Share a compliment.

angeloashmore and others added 10 commits July 20, 2026 21:06
Plain vitest evals that drive a real Claude Code agent (via the Agent SDK)
against the built CLI in a generic, hermetic config. Adds the `agent` fixture,
an `toHaveRun` matcher, an LLM-judge matcher with a never-regress ratchet, and a
per-run reporter that records efficiency metrics and the agent's prismic calls.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Merge the agent fixture, matchers, judge, config isolation, and result
recording into eval/it.ts; delete claude.ts, report.ts, and setup.ts.

Move the judge ratchet from a read-back of results.jsonl into an inline
threshold literal that vitest -u rewrites like a snapshot, so results.jsonl
is write-only. Trim recorded fields to effectiveness and effort (turns,
tokens, duration); drop cost, which swings with provider pricing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Judge uses the SDK's native structured output instead of hand-parsing JSON;
  score is range-checked to [0, 1]
- Create a throwaway Claude config dir per agent/judge call and clean it up;
  set persistSession: false so runs stay ephemeral
- Detect -u from vitest's update state; the worker's process.argv is empty
- Fix raiseLiteral rewriting the wrong literal on the callsite line, and
  support inline bars
- Fix the evals npm script so vitest also receives RUN_EVALS

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each eval now runs EVAL_TRIALS times (default 3) via it.for; a custom
reporter prints per-eval pass rates with infra failures excluded and
appends per-trial rows (pass, cost, tokens, turns, commands) to
results.jsonl. The judge is binary like agent-eval's: prose criteria,
pass/fail verdicts, no scores, no ratcheting bars. The full eval catalog
is implemented as it.skip drafts; only the rich-text smoke eval runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`node --run evals:report` prints one block per eval with one line per
run (pass rate, cost, mean tokens/turns/duration) and a latest-run
footer with best-of and per-trial rates. Rows now carry a per-run id so
the digest groups runs exactly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rows record the git SHA that produced them, shown in the digest so rate
changes point at a commit range. The file keeps the newest 100 runs; the
digest shows the last 10 per eval. results.jsonl merges with the union
driver so branch runs never conflict.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The judge now calls the Messages API directly with structured outputs
instead of spawning a Claude Code session. The agent fixture writes its
own task.meta via the built-in task context, trial rows no longer carry
a parsed trial number, and eval env vars are parsed with a zod schema.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Enable 26 validated evals with per-trial runs and skip 3 that fail
consistently, with the reason noted on each. Trim duplicate evals and
merge the two slice component evals into one.

Each eval now gets its own repository via an isolatedRepo fixture,
letting evals run 8-way concurrent without cross-test interference.
Global teardown sweeps orphaned repositories created by agents
(e.g. prismic init) that the harness cannot name in advance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add four evals from the Next.js happy-path docs: init with an existing
repository, page types vs custom types, commit-then-push flow, and
production previews after a deploy. The last two fail today and stay
enabled: evals are measurements, and their pass rates track CLI gaps.
Un-skip the other agent-capability evals for the same reason; only the
field rename eval stays skipped since the CLI cannot rename fields.

toHaveRun now evaluates each shell segment separately and ignores
--help invocations, so reading help no longer counts as running a
command in either direction.

Scope the results digest to the last EVAL_HISTORY runs (default 10)
with a per-run footer, and document the results.jsonl schema.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@angeloashmore angeloashmore changed the title feat: add agent evals test: add agent evals Jul 21, 2026
angeloashmore and others added 16 commits July 22, 2026 02:19
Adopts main's isolateRepo fixture API from #231: drops the serial test
project split, moves the exec fixture into eval/it.ts, and switches the
eval repo isolation to isolateRepo: true.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Evals only shell out to git; the generic runner's options plumbing was
left over from the pre-#231 CLI fixture. throwOnError surfaces setup
failures that previously passed silently.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… builds

Evals now require PRISMIC_ALLOW_EVALS=true and an explicit --project evals;
bare vitest runs only unit tests. Tests run against the prebuilt dist (no
MODE=test build), sync's poll interval is a validated PRISMIC_SYNC_POLL_MS
env var, agent-created repos are cleaned up per test instead of an
account-wide sweep, and dead MODE/DEV env plumbing is removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…elper

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Take main's test-build infra from #232 (CLI built in test global setup,
always-minified tsdown config) and keep the evals project config on top.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Export AgentRecord with field docs, build Row from it in the reporter,
and reuse that type in the report instead of a duplicated schema comment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n read

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vitest silently ignores `sequence` inside a `projects` entry, so every
test in a file ran serially. Root-level `sequence.concurrent` propagates
to projects and restores within-file concurrency.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lihbr
lihbr marked this pull request as ready for review July 24, 2026 01:28
Comment thread evals/it.ts
Comment thread evals/it.ts
Comment thread evals/reporter.ts

@lihbr lihbr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look good to me, I like what you did with Vite and using a custom reporter, the failing evals are also really insightful :)

Comment thread evals/it.ts Outdated
Comment thread eval/evals.eval.ts Outdated
…ILL.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit bee3782. Configure here.

Comment thread evals/it.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants