Add eval-ladder: a skill for designing and auditing an agent eval strategy - #127
Conversation
Distills benchflow-ai/awesome-evals (curated README + PATTERNS playbook) and
the primary sources it indexes into a retrievable skill for designing and
auditing eval architectures.
The skill is thin; the weight is in five references loaded on demand:
eval-surfaces.md the five gradable surfaces (output/trace/memory/
environment/mechanistic), why output-only grading is
weakest, and how to do environment-state grading with
collateral-damage assertions in both directions
judge-alignment.md error analysis (open -> axial coding -> prioritize),
binary judges, critique shadowing, and validating a
judge with TPR/TNR on held-out human labels rather
than raw agreement, which lies under class imbalance
metric-choice.md pass@k vs pass^k, floors segmented by what a scenario
guards (irreversible actions at 1.0, never a majority),
FAULT-vs-FAIL separation, saturation, must-not-fire
controls, cost as a metric
integrity-hazards.md the failure modes that live between tiers: tuning on
the gate, criteria drift, saturation, the harness
confound, contamination, offline-is-not-online
source-map.md the primary source behind each rule, plus the caveat
that load-bearing claims should be confirmed there
The load-bearing structure is an eight-rung ladder (structural, discriminating
corpus, code assertion, LLM judge, trajectory, environment state, sandboxed
cross-harness, human demonstration), each rung annotated with what it
structurally cannot prove, governed by two rules: descend before you ascend,
and grade the surface closest to the harm.
Cheap eval pack replaces the red-by-default stub with real checks on the three
clauses most likely to be softened by a well-meaning edit: that every rung
declares its blind spot (row count pinned at 8), that judge validation refuses
raw agreement in favour of TPR/TNR, and that the irreversible-action floor
stays at 1.0.
Same-PR inventory updates per the standing order: docs/testing.md gains
pack: eval-ladder/cheap, root README gains its row, docs/examples/PLAN.md
plugin count moves 24 -> 25, and the routing roster is regenerated.
Verified: cheap tier 1266 passed / 0 failed; counterfeit tier 22/0;
install-smoke + isolated pack green for eval-ladder.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1ggnuvK6SZgibp4P6SvVJ
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Demonstration —
|
The audit of this repo's own tiers (issue #119) turned on one move that the audit procedure did not actually instruct: executing a tier and counting what it reported, rather than reading its source. run-one.sh's header claims it "replicates run.sh section 10's sourcing contract exactly" — true on the page, false in one run, where 275 checks across 15 plugins never execute (#120). Question #5 already asked "how would it go red?", which is close but reads as a thought experiment. Make it operational: run the tier, compare the count of checks it reports against the count it contains, and treat any silent difference as the finding. Cheap tier 1266 passed / 0 failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V1ggnuvK6SZgibp4P6SvVJ
Correction to the demonstration, and one refinement it earnedCorrection. One of the "misses" I listed above is wrong, and since the demonstration is the artifact a reviewer merges on, it should not stand uncorrected. I wrote:
That is false.
I followed the skill and then reported doing so as a gap in it. Strike that item; the other four misses stand as written. Refinement (39edf52). Re-reading the audit to check that claim surfaced a real gap the demonstration did not name. The single move that produced Catch 1 — the So the audit procedure now says so explicitly:
This is the skill's own Cheap tier still 1266 passed / 0 failed. Generated by Claude Code |
There was a problem hiding this comment.
🟡 Changes recommended
The new cheap pack currently depends on helper functions not provided by the per-plugin runner used in required CI (so checks can silently not run), and docs/examples/PLAN.md has inconsistent coverage totals after the plugin count update.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds a new eval-ladder plugin to this marketplace, packaging a retrievable skill + references for designing and auditing agent eval strategies (tiered “ladder” model, surface-based grading, judge validation, and metric choice).
Changes:
- Introduces the
plugins/eval-ladder/plugin (skill, references, command, AGENTS/README, and a cheap eval pack). - Wires the new plugin into marketplace and evaluation routing/inventory (
marketplace.json, routing roster,docs/testing.md). - Updates top-level documentation indexes to include the new plugin (
README.md, examples plan counts).
File summaries
| File | Description |
|---|---|
| README.md | Adds eval-ladder to the plugin catalog table. |
| plugins/eval-ladder/.claude-plugin/plugin.json | New plugin manifest. |
| plugins/eval-ladder/README.md | New plugin README with invariant and references. |
| plugins/eval-ladder/AGENTS.md | New cross-harness usage + invariant writeup. |
| plugins/eval-ladder/commands/eval-ladder.md | New command entrypoint for invoking the skill. |
| plugins/eval-ladder/skills/eval-ladder/SKILL.md | New skill defining the ladder, audit procedure, and reporting guidance. |
| plugins/eval-ladder/skills/eval-ladder/references/eval-surfaces.md | Reference: gradable surfaces and state-based grading guidance. |
| plugins/eval-ladder/skills/eval-ladder/references/judge-alignment.md | Reference: error analysis, judge construction, and TPR/TNR validation. |
| plugins/eval-ladder/skills/eval-ladder/references/metric-choice.md | Reference: pass@k vs pass^k, floors, FAULT vs FAIL, controls, cost. |
| plugins/eval-ladder/skills/eval-ladder/references/integrity-hazards.md | Reference: cross-tier hazards (criteria drift, contamination, etc.). |
| plugins/eval-ladder/skills/eval-ladder/references/source-map.md | Reference: provenance map back to primary sources. |
| plugins/eval-ladder/evals/cheap/checks.sh | New cheap pack enforcing load-bearing invariants via structural greps/counts. |
| evals/routing/roster.txt | Adds the plugin to the routing roster. |
| docs/testing.md | Adds pack: eval-ladder/cheap to the testing inventory. |
| docs/examples/PLAN.md | Updates snapshot coverage count to reflect 25 plugins. |
| .claude-plugin/marketplace.json | Adds the eval-ladder plugin entry and aligns description text encoding. |
Review details
- Files reviewed: 18/18 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The cheap tier's landing-page sync gate arrived on main while this branch was open (docs/build-index.sh + the "landing page — in sync with the marketplace" section). Adding eval-ladder to marketplace.json therefore left the generated landing page stale, which is what CI caught on both prior heads: FAIL landing: docs/index.html is STALE — run docs/build-index.sh Merged origin/main (clean, no conflicts) and regenerated the page with the repo's own builder rather than editing it by hand. Worth recording, because it is the same lesson this branch's skill is about: the local cheap run showed 1266 passed / 0 failed and CI showed 1267 passed / 1 failed on identical source. The difference was not the code — CI grades the merge commit, so it ran a gate this checkout did not have. A green from a tier is bounded by the tier that actually ran. Verified on the merged head: cheap 1268 passed / 0 failed, counterfeit 22 / 0, install-smoke 10 / 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V1ggnuvK6SZgibp4P6SvVJ
… split Two findings, both verified before fixing. 1. plugins/eval-ladder/evals/cheap/checks.sh depended on has/hasE/lacksE from the runner. run.sh defines them; run-one.sh — the runner the REQUIRED install matrix uses — does not, and packs are sourced without `set -e`, so every such call printed "command not found" and the run continued green. Measured on this pack: 32 dropped calls, reported as "12 passed, 0 failed". Defined the three helpers locally, guarded with `declare -F` so the runner's own definitions still win where it has them. This pack now reports 44 passed / 0 failed under run-one.sh, up from 12. voice solves this by defining them unconditionally; the guarded form is used here so a future shared preamble is not shadowed. The systemic fix — 275 dropped checks across 15 other plugins — stays in #120, which this PR deliberately does not widen into. 2. docs/examples/PLAN.md: the snapshot row moved to 25/13 but the "Coverage today" prose still split 12/12 and omitted eval-ladder from the no-pack list. Corrected to 13, and the paragraph's closing claim that "these counts cannot silently go stale" is narrowed to what is actually machine-checked: the cheap tier verifies the snapshot row, not this prose — which is exactly how it went stale. Verified: cheap 1268 passed / 0 failed, counterfeit 22 / 0, install-smoke 10 / 0, run-one.sh eval-ladder 44 / 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V1ggnuvK6SZgibp4P6SvVJ
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e3a2a89193
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…hmetic Four findings, all verified before fixing. The fifth (marker helpers) was already fixed in 9d22fd3. 1. The ladder-table check counted rows and claimed to verify blind spots. `grep -cE '^\| [0-7] \| \*\*'` stays at 8 when a "structurally cannot" cell is emptied, or when one rung number is duplicated and another deleted — so the check asserted more than it verified, which is the exact defect this plugin exists to name. Replaced with a parse of all four cells: rungs must be exactly 0-7, each once, every blind-spot cell non-empty and non-vacuous. Mutation-tested four ways, all rejected: emptied cell, "Nothing", duplicate rung number, deleted rung. 2. Rung 7's blind spot read "Nothing — and nothing can machine-enforce it", claiming a single human demonstration has no evidentiary limit. That contradicts this plugin's own invariant and its ban on "fully tested". Now: "That one run generalizes — no repeatability, no unseen inputs". The not-machine-enforceable point already lives in the prose, where it belongs. 3. gen-roster.sh splits the description at the first ". " or ": ", so the roster line was "Design and audit the eval ladder for an agent system:" — a dangling colon with every distinguishing term discarded, in the prompt the routing tier uses to pick a specialist. Reworded so the first sentence is substantive and fits the 200-char cap intact. Roster regenerated. Not fixed here: agent-compiler and dev-diary truncate the same way. That is a generator defect predating this branch; recorded on #119 rather than widened into. 4. docs/examples/PLAN.md: the previous commit's 12 -> 13 edit made the row arithmetically false — 13 no-pack plugins plus 3 packed is 16, but there are 15 snapshots, because eval-ladder has none. Now states 12 of the 13. Also reverted unrelated churn this branch introduced: scaffold-plugin.sh rewrites marketplace.json with json.dumps default escaping, which converted 12 other plugins' literal em dashes to —. Restored main's formatting and re-added only the eval-ladder entry, so the marketplace diff is 18 insertions and 0 deletions. Verified: cheap 1268 passed / 0 failed, counterfeit 22 / 0, install-smoke 10 / 0, run-one.sh eval-ladder 44 / 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V1ggnuvK6SZgibp4P6SvVJ
There was a problem hiding this comment.
🟡 Changes recommended
The landing page meta description is now internally inconsistent/misleading by implying every plugin has before/after examples (see stored comment on docs/index.html:7).
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 19/19 changed files
- Comments generated: 1
- Review effort level: Lite
| <meta name="viewport" content="width=device-width, initial-scale=1"> | ||
| <title>agent-plugins — a cross-harness agent plugin marketplace</title> | ||
| <meta name="description" content="24 plugins for Claude Code and any coding agent that reads SKILL.md and AGENTS.md, each with evals, real before/after examples, and a design record."> | ||
| <meta name="description" content="25 plugins for Claude Code and any coding agent that reads SKILL.md and AGENTS.md, each with evals, real before/after examples, and a design record."> |
eval-ladder landed on main with declare -F-guarded local copies of has/hasE/ lacksE — a workaround added because run-one.sh defined only three of the six helpers. That block carried an explicit note to delete it once the shared preamble arrived; this is that change, so the workaround goes with it. Its pack still reports 44 passed / 0 failed under run-one.sh, now inheriting the helpers from evals/cheap/helpers.sh like every other pack. The parity gate's "no plugin pack redefines a shared helper" check covers it from here. Verified on the merged head: cheap 1291 passed / 0 failed, counterfeit 23 / 0, install-smoke green, and all 25 registered plugins clean under run-one.sh with zero "command not found". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V1ggnuvK6SZgibp4P6SvVJ
evals/cheap/run.sh defined six check helpers; evals/cheap/run-one.sh — the runner the REQUIRED install matrix uses — defined three. Packs are dot-sourced without `set -e`, so every call to a missing helper printed "command not found" on stderr and execution sailed past it, and the runner then reported a green summary over checks that never ran. Measured across every registered plugin, isolated runner, before -> after: 634 -> 887 checks. 253 checks across 14 plugins were never running in the required tier, redgate alone dropping 54 of its 90. The dropped ones were disproportionately the invariant-verbatim assertions, because that is exactly what has/hasE exist for. The full run.sh sweep was unaffected throughout, which is why this was invisible: nothing compared the two runners' counts. The fix, in four parts: 1. evals/cheap/helpers.sh — one definition of all six helpers, sourced by both runners, with neither defining any locally. 2. A fail-closed guard around pack sourcing: a pack calling a helper NO runner defines is a recorded failure rather than a silent skip. The handler records to a temp file and the parent raises the failure, because bash runs command_not_found_handle in a forked subshell where a counter increment would be lost. 3. Cheap tier 10b, a parity gate: both runners must source helpers.sh, define no local helper, and ENCLOSE the pack source in the guard — validated structurally, since token presence says nothing about ordering. 4. Counterfeit fixture 18-silent-helper-skip, driven through BOTH runners via a new ALSO_RUN_ONE declaration, with a calibration step requiring the baseline to be green under the isolated runner too. The other seventeen fixtures mutate plugin content; this one mutates the pack-to-harness relationship, which is why the corpus never caught this itself. Also deleted the two local helper copies (voice's, and eval-ladder's declare -F-guarded workaround from #127), leaving one definition in the repo. This guard and its checks were wrong four separate ways during review — the lost subshell counter, an unhandled mktemp failure that silently disabled it, a clobbered pre-existing handler, and a gate that grepped where it should have parsed. Three of the four were found by reviewers. A fail-closed mechanism has more failure modes than the thing it guards, which is the argument for fixture 18 existing at all. Found by auditing this repo with the eval-ladder skill (#119, finding F1). Closes #120 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Distills benchflow-ai/awesome-evals — the curated README (10 sections) and its
PATTERNS.mdplaybook — plus the primary sources it indexes, into a retrievable skill for designing and auditing eval architectures.Assessment produced by running the skill against this repo: #119, with sub-issues #120–#126.
What lands
A new
eval-ladderplugin. The skill is deliberately thin; the weight is in five references loaded on demand:SKILL.mdreferences/eval-surfaces.mdreferences/judge-alignment.mdreferences/metric-choice.mdreferences/integrity-hazards.mdreferences/source-map.mdThe load-bearing structure is an eight-rung ladder — structural, discriminating corpus, code assertion, LLM judge, trajectory, environment state, sandboxed cross-harness, human demonstration — each annotated with what it structurally cannot prove, governed by two rules: descend before you ascend (never buy a judge for what a predicate can decide) and grade the surface closest to the harm.
The invariant
Cheap eval pack
Replaces the scaffold's red-by-default stub with real checks on the clauses a well-meaning edit is most likely to soften: that judge validation refuses raw agreement in favour of TPR/TNR, that the irreversible-action floor stays at 1.0, and that every rung of the ladder declares a real blind spot. That last check parses all four table cells — rungs exactly 0–7, each once, every "structurally cannot" cell non-empty and non-vacuous — and is mutation-tested four ways (emptied cell,
"Nothing", duplicate rung, deleted rung), all rejected. Plus a provenance floor: the source map must carry ≥20 primary-source URLs, so it cannot be thinned to assertions.The pack defines
has/hasE/lacksElocally, guarded withdeclare -F, so it runs whole underrun-one.shas well asrun.sh— see the Copilot thread and #120.Same-PR inventory updates (standing order)
docs/testing.mdgainspack: eval-ladder/cheapREADME.mdgains its rowdocs/examples/PLAN.mdplugin count 24 → 25, prose split corrected to 12/13, and the snapshot row corrected to "12 of the 13" (eval-ladder has no snapshot — it has no behavioral pack to capture one from)evals/routing/roster.txtregenerateddocs/index.htmlregenerated withdocs/build-index.shafter mergingmainVerification
On the current head (
30a4b5b), local:evals/cheap/run.sh)evals/counterfeits/run.sh)ci/install-smoke.sh eval-ladder)evals/cheap/run-one.sh eval-ladder)CI on
bc0f2c4was green across every job; the branch has since mergedmainand ismergeable_state: clean.No paid tier was run and no API keys were used, so this PR carries no evidence about behavioral, routing or deep-tier pass rates — only that those legs correctly declined to run under their path filters.
Three things this PR got wrong first, recorded because they are the point
CI grades the merge commit. The first two heads went red on
landing: docs/index.html is STALEwhile local said1266 passed, 0 failed.mainhad gaineddocs/build-index.shand a landing-page sync gate mid-branch, so CI ran a check this checkout did not contain. Fixed by mergingmainand regenerating with the repo's own builder.The new pack was doing the thing the skill forbids. It relied on helpers
run-one.shdoes not define, so the required install matrix reported12 passed, 0 failedover 44 checks. Caught by the audit, then independently by Copilot.A check that claimed more than it verified. The ladder-table check counted rows while its heading asserted every rung declares a blind spot — a row count survives an emptied cell and a duplicate-plus-delete. Caught by Codex; replaced with a full cell parse and mutation-tested.
All three are the same failure: a green bounded by something other than what it appears to cover. That is the invariant this plugin exists to state, and it caught its own author three times.
Deliberately not in this PR
The systemic version of #2 —
evals/cheap/run-one.shsilently drops 249 checks across 14 other plugins (redgatealone drops 54), so the required install tier certifies them over a subset. Tracked in #120 with the reproduction and full blast radius. Not fixed here, perscope-fence: a finding outside the stated task is recorded, not fixed in the same change. Per-pack guards like this one are a workaround, not the fix. Happy to do the shared-preamble fix in a follow-up immediately.Also recorded rather than fixed:
gen-roster.shtruncatesagent-compileranddev-diarydescriptions to a dangling colon, contributing near-zero routing signal for both (#119, F14).Open review thread
Codex suggested adding an
eval-ladderscenario to the routing pack. I took the half of that finding about the roster description and declined the scenario, leaving the thread open for a human rather than resolving it myself — reasoning in the thread: the pack has 11 must-fire scenarios for 25 plugins, the tier is advisory and costs real spend, and a scenario written by a skill's own author and graded by a regex they also write is the tuning-on-the-gate pattern this plugin's own hazards reference warns about.Demonstration comment below, per the demonstration discipline, with a correction to it in the comment after.
🤖 Generated with Claude Code
https://claude.ai/code/session_01V1ggnuvK6SZgibp4P6SvVJ