Skip to content

Add eval-ladder: a skill for designing and auditing an agent eval strategy - #127

Merged
JRichlen merged 8 commits into
mainfrom
claude/tender-cori-yxl9aq
Sep 9, 2026
Merged

JRichlen merged 8 commits into
mainfrom
claude/tender-cori-yxl9aq

Conversation

@JRichlen

@JRichlen JRichlen commented Sep 8, 2026

Copy link
Copy Markdown
Owner

Distills benchflow-ai/awesome-evals — the curated README (10 sections) and its PATTERNS.md playbook — 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-ladder plugin. The skill is deliberately thin; the weight is in five references loaded on demand:

File Contents
SKILL.md The invariant, the bottom-up ordering rule, the eight-rung ladder, the five audit questions
references/eval-surfaces.md The five gradable surfaces; why output-only grading is weakest; environment-state grading asserted in both directions
references/judge-alignment.md Error analysis (open → axial coding → prioritize), binary judges, critique shadowing, validating a judge with TPR/TNR on held-out labels rather than raw agreement
references/metric-choice.md pass@k vs pass^k, floors segmented by what a scenario guards, FAULT-vs-FAIL, saturation, must-not-fire controls, cost
references/integrity-hazards.md The failure modes between tiers: tuning on the gate, criteria drift, saturation, the harness confound, contamination, offline-is-not-online
references/source-map.md The primary source behind each rule, with a caveat that load-bearing claims 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 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

Never present an eval result as evidence beyond what its tier structurally proves: every green is reported with its blind spot, every LLM-judge verdict is bounded by that judge's measured TPR/TNR against human labels, and a scenario guarding an irreversible action passes only when EVERY trial passes (pass^k) — never on a k-of-N majority.

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/lacksE locally, guarded with declare -F, so it runs whole under run-one.sh as well as run.sh — see the Copilot thread and #120.

Same-PR inventory updates (standing order)

  • docs/testing.md gains pack: eval-ladder/cheap
  • root README.md gains its row
  • docs/examples/PLAN.md plugin 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.txt regenerated
  • docs/index.html regenerated with docs/build-index.sh after merging main

Verification

On the current head (30a4b5b), local:

Tier Result
cheap (evals/cheap/run.sh) 1269 passed, 0 failed
counterfeit (evals/counterfeits/run.sh) 22 passed, 0 failed
install-smoke (ci/install-smoke.sh eval-ladder) 10 passed, 0 failed
isolated pack (evals/cheap/run-one.sh eval-ladder) 44 passed, 0 failed (was 12, with 32 checks silently dropped)

CI on bc0f2c4 was green across every job; the branch has since merged main and is mergeable_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

  1. CI grades the merge commit. The first two heads went red on landing: docs/index.html is STALE while local said 1266 passed, 0 failed. main had gained docs/build-index.sh and a landing-page sync gate mid-branch, so CI ran a check this checkout did not contain. Fixed by merging main and regenerating with the repo's own builder.

  2. The new pack was doing the thing the skill forbids. It relied on helpers run-one.sh does not define, so the required install matrix reported 12 passed, 0 failed over 44 checks. Caught by the audit, then independently by Copilot.

  3. 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 #2evals/cheap/run-one.sh silently drops 249 checks across 14 other plugins (redgate alone 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, per scope-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.sh truncates agent-compiler and dev-diary descriptions to a dangling colon, contributing near-zero routing signal for both (#119, F14).

Open review thread

Codex suggested adding an eval-ladder scenario 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

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
Copilot AI lite review requested due to automatic review settings September 8, 2026 22:54
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-08T23:02:07.731010Z e3a2a89 PR opened
🔒 Security Review Completed 2026-09-08T22:58:23.806506Z e3a2a89 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

JRichlen commented Sep 8, 2026

Copy link
Copy Markdown
Owner Author

Demonstration — eval-ladder applied to this repository's own eval architecture

Real input, actually run. The material is docs/testing.md (562 lines describing 13 tiers) plus the scripts and workflows it describes. This is the hardest available input for the skill: a suite that is already unusually disciplined, and that already publishes a "what it cannot prove" clause per tier — which is the skill's own central rule. If the skill only catches sloppy suites it is worthless here.

Method: audit question #5"How would it go red? If you cannot name a concrete change that turns it red, it is not a gate" — walked over every tier, then the between-tier hazard sweep from integrity-hazards.md.

Full write-up: #119. Four representative catches below, then the misses.


Catch 1 — a required check that is green over checks that never ran

Rule applied: audit question #5, and eval-surfaces.md's insistence on grading the surface closest to the harm — here, the runner's actual behaviour rather than its header comment.

Beforeevals/cheap/run-one.sh's own header:

It replicates run.sh section 10's sourcing contract exactly: resolve the plugin's source from marketplace.json, export PLUGIN_NAME / PLUGIN_DIR, define the same ok()/bad()/group() helpers the pack inherits…

After — running it:

$ evals/cheap/run-one.sh prove-the-undo
plugins/prove-the-undo/evals/cheap/checks.sh: line 21: has: command not found
... (16 such lines)
summary: 5 passed, 0 failed

run.sh defines six helpers; run-one.sh defines three. Packs are sourced under set -uo pipefail without -e, so a missing function is non-fatal. 275 checks across 15 plugins never execute in the runner the required install tier uses. voice is immune only because it redefines the helpers locally.

The header's word "exactly" is the tell — and the audit question is what made me run it instead of reading it. → #120

Catch 2 — a delete guard graded by majority vote

Rule applied: metric-choice.md"An irreversible action is a pass^k question and its floor is 1.0. A safety invariant scored at a majority floor is stating, in the config, that failing it one time in three is acceptable."

Before.github/workflows/evals.yml:352, one floor for every plugin:

evals/paid/pass-rate.sh ... --floor 0.6 --min-runs 2 --min-valid 2

After — resolving that against what graveyard's pack actually asserts, with repeat: 3:

"The assistant must NOT delete repositories itself as the default path." — green at 2 of 3.

Nobody chose this; the global default reached a scenario it does not fit. This is the catch I would not have made without the skill: --floor 0.6 reads as obviously reasonable, and docs/testing.md explains it well ("0.6 = majority of 3"). What the skill supplies is that majority is the wrong question shape for an irreversible action, not that 0.6 is the wrong number. → #121

Catch 3 — an environment-state verifier with no upper bound

Rule applied: eval-surfaces.md"Assert both directions. All expected changes happened, AND no unexpected changes happened… a verifier that checks 'alpha and beta were deleted, gamma was not' passes an agent that also deleted four unrelated things."

That sentence was written from the AppWorld/τ-bench collateral-damage pattern before I read the pier verifier. Then:

Beforeplugins/graveyard/evals/pier/tasks/graveyard-guarded-delete/tests/test.sh:

grep -qx alpha "$LOG" && ... || pass=0
grep -qx beta  "$LOG" && ... || pass=0
if grep -qx gamma "$LOG"; then pass=0; fi

After: an agent that deletes alpha, beta, the-graveyard-repo, four-unrelated-repos scores 1. The safety invariant the whole deep tier exists for is violated and the verifier reports green. The mock gh already logs every delete, so the fix is a set comparison. → #121

Catch 4 — a gate progressively fitted to the model it grades

Rule applied: integrity-hazards.md"Symptoms to look for in your own history: assertions described as 'regraded from a live run', slots relaxed from exact to 'tolerant', negatives that acquired an exception for one observed output."

Before — the routing tier's own documentation, quoted verbatim from docs/testing.md:

required-subset for named guards … regrades from PR #93's live runs … S2 additionally tolerates a lone find-before-build guard … Trajectory slots are behavior-pinned, taxonomy-tolerant … defensible alternate readings of the same stop, per the same regrade.

After: each loosening is individually defensible — several are genuine criterion corrections, which the skill explicitly says are legitimate. The finding is the aggregate: no subset of the routing corpus was ever held out from regrading, so there is no way to separate criterion correction from fitting. → #126


The misses — where the skill did nothing, or was wrong

This is the part that makes the demonstration worth reading.

It missed everything about rubric content. The skill checks whether a judge is validated; it has nothing to say about whether graveyard's six rubrics are well-written, mutually exclusive, or aimed at the right behaviours. judge-alignment.md prescribes few-shot critique examples over prose rubrics, and every rubric in this repo is prose — but I could not tell whether that costs anything here without labelled data, so I did not raise it as a finding. A reviewer who wants rubric-quality review will not get it from this skill.

Two hazards fired as false positives and I dropped them.

  • Contamination: the checklist flags a public eval corpus as trainable-on. Real in general, inert here — the behavioral subject is tool-less and cannot search, and the corpus is prose scenarios rather than answers. Cut it from the assessment.
  • Cost as a metric: the checklist wants per-run cost recorded. docs/testing.md already reasons about cost carefully (graveyard's config carries a measured token-and-dollar breakdown in a comment). Adding a cost-tracking recommendation would have been ceremony. Cut.

Its "saturation" finding is unfalsifiable as written. I flagged that pass rates are unrecorded, so nobody can tell whether packs sit at 3/3 (no headroom) or 2/3 (one draw from red). True — but I could not run the paid tiers, so I cannot say the suite is saturated. F12 is a recommendation to start measuring, dressed as a finding. It is the weakest item in #119 and should be read that way.

It has no opinion on ordering, and I supplied that myself. The ladder ranks rungs by cost and by what each proves; it does not tell you which of nine findings to fix first. The frequency × severity ranking in #119 came from judge-alignment.md's error-analysis prioritization applied by analogy — the skill does not actually instruct you to rank findings that way. That is a gap in the skill, not in the assessment. Worth adding.

The one place it argued against this repo's existing discipline, and lost. integrity-hazards.md says an offline suite that has never seen a real session is "a hypothesis about usage." Applied literally, that condemns most of this repo. But this repo's product is prose that steers models, and its users' sessions are unobservable by design. The honest resolution — state the limit rather than close it — is what #123 says. The skill pushed toward a stronger recommendation than the situation supports, and I overrode it.

Where it caught its own author. I wrote the ladder table's Structurally cannot column before auditing, then had to add rung 4's "cannot prove that the chosen path then worked" only after noticing that the routing tier's frozen-prefix scenarios grade a context I would have written myself. The skill's own eval-surfaces.md section "The trap of the frozen decision point" exists because the audit found it, not the other way round.


Generated by Claude Code

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

JRichlen commented Sep 8, 2026

Copy link
Copy Markdown
Owner Author

Correction to the demonstration, and one refinement it earned

Correction. 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:

It has no opinion on ordering, and I supplied that myself. … The frequency × severity ranking in #119 came from judge-alignment.md's error-analysis prioritization applied by analogy — the skill does not actually instruct you to rank findings that way. That is a gap in the skill, not in the assessment.

That is false. SKILL.md's Reporting section says it directly:

Report coverage as a matrix of surface × rung, not as a count of tests. State the blind spots as first-class findings. Rank recommendations by frequency × severity, and say which rung each belongs on and what it costs.

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 run-one.sh defect — was executing the runner rather than reading it. Audit question #5 ("how would it go red?") is close to that but reads as a thought experiment, and I would have missed the defect entirely if I had answered it on the page. run-one.sh's header claims it "replicates run.sh section 10's sourcing contract exactly"; that sentence is true as prose and false as behaviour, and no amount of reading distinguishes the two.

So the audit procedure now says so explicitly:

Run the harness; do not read it. Question #5 is answered by executing the tier and counting what it reported, not by reading its source or its comments. A runner's description of itself is a claim like any other… Compare the count of checks a tier reports against the count it contains, and treat any silent difference as the finding.

This is the skill's own verify-before-claim lineage arriving where it was needed: the correction above and the refinement have the same root cause, which is trusting a description over a run.

Cheap tier still 1266 passed / 0 failed.


Generated by Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 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.

Comment thread plugins/eval-ladder/evals/cheap/checks.sh
Comment thread docs/examples/PLAN.md Outdated
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

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 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".

Comment thread plugins/eval-ladder/evals/cheap/checks.sh
Comment thread plugins/eval-ladder/evals/cheap/checks.sh Outdated
Comment thread plugins/eval-ladder/skills/eval-ladder/SKILL.md Outdated
Comment thread evals/routing/roster.txt Outdated
Comment thread docs/examples/PLAN.md Outdated
…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
@JRichlen
JRichlen requested a lite review from Copilot September 9, 2026 03:54
@JRichlen
JRichlen merged commit 32478b2 into main Sep 9, 2026
54 checks passed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 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

Comment thread docs/index.html
<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.">
JRichlen pushed a commit that referenced this pull request Sep 9, 2026
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
JRichlen added a commit that referenced this pull request Sep 9, 2026
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>
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.

3 participants