Conversation
`claude plugin eval` runs a plugin against realistic prompts and, crucially, runs the same prompts again with no plugin loaded, so a case reports what the plugin contributed rather than what Claude can do anyway. That is the question evals/README.md's layer 3 was written to ask and had no automated answer for. Adds one worked case, search-charts/finds-a-map-link, a `make behaviour` target, and the guidance for writing the next one. .claude-plugin/plugin.json is new and load-bearing: without a plugin manifest the command resolves no plugin, evaluates plain Claude Code, and reports a meaningless delta. It carries no version, like everything else here, which makes `claude plugin validate` warn — documented so nobody silences it. The case took three attempts to measure anything, and the two dead ends are written up because they are the failure mode this tool is for: a famous chart slug the baseline recalls from memory, then an obscure one the baseline finds anyway once WebFetch is granted. Only a grader on the route — does Claude call the documented /api/search endpoint — separates the arms. Measured over 6 runs: with 1.00, without 0.67, delta +0.33, $0.78. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
claude plugin evalruns a plugin against realistic prompts and then runs the same prompts again with no plugin loaded, reporting the difference. That second arm answers the questionevals/README.md's layer 3 was written to ask and had no automated answer for: did the skill do this, or would Claude have managed anyway. This adds one worked case, amake behaviourtarget, and the guidance for writing the next one. No skill content changes.Skim the code — it's one case, one make target and a manifest. The prose in
evals/README.mdis the actual deliverable and is where review effort is worth spending.How it works here
A case is a directory under
evals/; these live atevals/skills/<skill>/<case>/so they sit with that skill's other eval inputs.prompt.mdholds the prompt and the run's limits, each file undergraders/is one pass/fail check. The runner already ignoreslib/,results/and the per-skill directories, since none of them contain aprompt.md.make behaviourgrants exactlyWebFetch(domain:ourworldindata.org). Narrow in both directions on purpose: the cases need it, and the baseline arm gets it too, so a positive Δ is the skill's doing rather than the grant's. GrantingBashwould be closer to how these skills really run — they documentcurlandjq— but it puts every command under Claude Code's OS sandbox, whose preconditions are machine-dependent; see the appendix..claude-plugin/plugin.jsonis new and load-bearing. Without a plugin manifest the command resolves no plugin, silently evaluates plain Claude Code, and reports a Δ that means nothing. It carries noversion, like everything else here, soclaude plugin validatenow emits one warning — documented inAGENTS.mdso nobody "fixes" it by adding a version.The case took three attempts, and that's the point
Two of the three are written into
evals/README.mdbecause they are exactly the failure mode this tool exists to expose:WebFetchgranted, baseline Claude just browsed the site/api/searchendpointThe shipped case keeps all three graders. The two answer graders pass in both arms — they are regression guards on the
?tab=mapping the skill documents, not evidence the skill helps, and the Δ says so honestly.Verified
Ran the case at the default three runs per arm:
make validatepasses (with the documented version warning).make testandmake triggersare untouched by this change and were not re-run.Not verified: CI. This adds no workflow — running it on every PR would spend real money per run, and which cadence is worth it is a decision, not a default.
Decisions worth a second look
Layers 2 and 3 now overlap. A
tool_used: Skillgrader asks the same question as a trigger eval, on one prompt instead of ten, andclaude plugin evalis first-party whererun-trigger-eval.pyis ~400 lines of ours. I did not fold layer 2 in — ten prompts per skill measures routing at a width one case can't, and the call is easier to make with a few more behaviour cases in hand. It's noted as an open question in the caveats rather than silently left.evals.jsonis now legacy. Each skill still has one, holding hand-run cases in the agentskills.io format. I left them and added a line saying to port a case into the directory format when you next touch that skill, rather than doing four ports in a PR about the mechanism.Context for whoever picks this up
Bashfails on a Mac with Docker Desktop. The sandbox refuses with "the Docker (~/.docker, DOCKER_CONFIG) credential store on this machine holds a symbolic link inside it", and the case records a run error rather than a score. PointingDOCKER_CONFIGat a clean directory does not help — it checks~/.dockerregardless. This is why the case usesWebFetch.file_existsonly sees files Claude created, not ones it edited.evals/results/<timestamp>/with a self-containedreport.htmlshowing each grader's verdict per run. Already gitignored.🤖 Generated with Claude Code