WCAG 2.2 AA audit — perceivable, operable, understandable, robust criteria. Deep-dive for /launch-check accessibility.
日本語の概要は準備中です。原文の説明を表示しています。
Score a review agent (Rex/Hakim/Tariq) against a labeled PR corpus — ground-truth defect-set overlap, never a prose rubric. Headline metric is approve-precision.
日本語の概要は準備中です。原文の説明を表示しています。
実行スコア 未検証
インストール方法を見るインストールする前に、エージェントに与えられる指示の中身を確認できます。
Writing: read docs/skills/shared/writing-rule.md before you write a durable artifact.
Scores one of the framework's review agents (Rex / Hakim / Tariq) against a labeled corpus of real, already-reviewed PRs with frozen ground-truth defect sets. Reports catch-rate, false-positive-rate, and approve-precision (the headline metric) against a configurable pass/fail threshold.
Spike #825 tested that shape first — score a review agent's text output against a 0-4 rubric, calibrated against recorded verdicts — and found it at chance on the one question that matters: was an approval justified? A fluent, verification-heavy wrong approval scored as well as or better than genuinely correct approvals (docs/spike-825/findings.md). This skill instead:
Full rationale: AgDR-0089. Corpus format: docs/eval-agents/SCHEMA.md.
/eval-agents rex # run against the seeded starter corpus
/eval-agents hakim --corpus docs/eval-agents/corpus/hakim.json
/eval-agents tariq --corpus my-corpus.json # Use a custom Tariq corpus.
/eval-agents rex --check-only # validate corpus schema only, no spawns
<agent> is one of rex, hakim, tariq — the three review agents that produce a verdict over a diff and share the APPROVED / CHANGES REQUESTED / COMMENT vocabulary. Naqid is out of scope for v1 (it challenges premises, not diffs — no defect-set structure to score against; see AgDR-0089 § Decision point 7).
--suggest-entries is not a mode of this skill. #833 left corpus mining for a separate ticket. #1440 adds it as a separate, read-only script. See "Mining candidate entries" below.
lib/mine_disagreements.py finds candidate Rex MISS entries in the review history of merged PRs. It prints candidates for a human to adjudicate. It never writes a corpus file.
python3 .claude/skills/eval-agents/lib/mine_disagreements.py --repo <owner/repo> [--limit 60] [--format json|markdown]
The script mines one shape:
That shape is the independent_review oracle (AgDR-0089). The script does not mine the reverse shape (Hakim or Tariq APPROVED, Rex CHANGES). Rex's scope is a superset of Hakim's scope, because Rex also enforces the writing standard. So the reverse shape mostly shows two reviewers that do different jobs. The #1440 hand adjudication found zero Hakim misses in five reverse-shape candidates.
How the script reads a review:
## Code Review, ## Security Review, ## Design Review). All agents post under one account, so the GitHub author does not identify the agent.### Verdict section. When no such heading exists, it reads a **Verdict**: line. It never searches the full body, because recap prose caused all false positives in the #1440 probe. The verdict line must start with a verdict word. A line such as "No blocking findings remain." gives an unknown verdict.**Commit**: line first, then the Reviewed commit: footer. It uses the API commit_id only when the body names no commit. It ignores "Last reviewed commit:" and "Last-reviewed commit:".Each candidate has a partial draft_entry and an evidence block with links to the approval and the challenge. The draft has no ground_truth_defects and no recorded_verdict.was_justified. Thus validate-corpus.sh rejects a raw paste. To promote a candidate, follow docs/eval-agents/README.md § "Growing the corpus".
The script uses only read calls: one PR list call and one GET call to the reviews API for each PR. It reads PR reviews only. It does not read PR conversation comments.
This is the load-bearing safety property of the whole skill. The real review agents are hard-wired (.claude/agents/code-reviewer.md § "HARD STOP" and the equivalent sections in security-reviewer.md / solution-architect.md) to fetch a live PR, post a real GitHub review comment, and write a real approval marker. An eval run that spawned one of these agents against a real historical PR number would spam an already-merged PR with an eval-only comment and pollute .claude/session/reviews/.
/eval-agents never gives the agent-under-test a PR number, a repo, or a tracker reference. Every spawn is invoked with a bespoke eval prompt over sanitized, standalone diff text. See Step 3 below.
⛔ MUST NOT be CI-gated until eval spawns can be tool-restricted
Harness-capability finding (#833, investigated per Hakim's security review of #828): the strongest version of this mitigation would remove
Bashfrom the agent-under-test's spawn entirely — the diff is handed inline in the prompt, so a re-review spawn has no legitimate need for shell access, and removing it converts every contamination vector in this file from "instructed not to" into "cannot." That requires per-spawn tool subsetting: theAgenttool call that/eval-agentsissues would need to override the targetsubagent_type's tool list for just this invocation.The
Agenttool does not expose this. Its parameters aredescription,isolation,model,prompt,run_in_background, andsubagent_type— there is notools/allowed_toolsoverride. Tool access is fixed persubagent_type, declared in that agent's own definition file (.claude/agents/code-reviewer.mdetc. carry aTools:frontmatter line), not overridable by the caller. This is the same shape of gap.claude/rules/agent-role-selection.mddocuments for the spawn boundary generally (AgDR-0056: "the current hook plumbing cannot reliably see into a sub-agent boundary") — here it's the tool grant, not a hook, that the spawn boundary can't touch.A heavier alternative exists — ship dedicated, Bash-free eval-mode agent definitions (
code-reviewer-eval.mdetc.) that/eval-agentsspawns instead of the realcode-reviewer/security-reviewer/solution-architect— but that means forking and hand-maintaining a second copy of each review checklist, which drifts from the real agent over time and is a materially bigger project than this hardening ticket's scope. Left as a follow-up if/when/eval-agentsneeds to move off manual/periodic use.Until per-spawn tool subsetting exists (or the dedicated-agent-definition alternative ships), the two-check contamination backstop in Step 3e (snapshot-diff + text-scan) is a mitigation, not a hard sandbox — the agent-under-test retains normal
Bashaccess and could in principle still attempt a tracker call./eval-agentsMUST NOT be wired into CI, a merge gate, a pre-commit hook, or any unattended/scheduled run until this gap closes. Rule 6 below already scopes v1 to manual/periodic use for a different reason (#824's out-of-scope); this finding is an additional, independent reason the same restriction must hold.
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-read-config.sh"
AGENT="$1" # rex | hakim | tariq
case "$AGENT" in
rex|hakim|tariq) ;;
*) echo "/eval-agents: unknown agent '$AGENT' — must be rex, hakim, or tariq" >&2; exit 2 ;;
esac
CORPUS_DIR=$(config_get '.eval_agents.corpus_dir' 'docs/eval-agents/corpus')
CORPUS_PATH="${CORPUS_FLAG:-$CORPUS_DIR/$AGENT.json}"
if [ ! -f "$CORPUS_PATH" ]; then
echo "/eval-agents: no corpus at $CORPUS_PATH." >&2
echo " $AGENT has no seeded starter corpus — pass --corpus <path> explicitly." >&2
exit 3
fi
tariq.json contains 8 seeded entries (see docs/eval-agents/README.md).
/eval-agents tariq uses this starter corpus unless the operator supplies --corpus.
bash .claude/skills/eval-agents/lib/validate-corpus.sh "$AGENT" "$CORPUS_PATH"
Abort on any schema violation — do not spend a real agent spawn scoring against a malformed corpus. --check-only stops here: print the validator's entry-count summary and exit 0.
For each entry in the corpus:
Try local git first (fast path); fall back to the GitHub API (works regardless of local reachability — many review-round commits are squashed away from local history after merge, but remain queryable by SHA via the API indefinitely):
if git cat-file -e "$COMMIT" 2>/dev/null; then
DIFF_TEXT=$(git diff "$DIFF_RANGE")
else
DIFF_TEXT=$(gh api "repos/$REPO/commits/$COMMIT" -H "Accept: application/vnd.github.v3.diff" 2>/dev/null)
fi
if [ -z "$DIFF_TEXT" ]; then
echo "⚠ $ENTRY_ID: could not resolve diff for commit $COMMIT — skipping, flagged UNRESOLVABLE in report" >&2
continue
fi
Also capture the changed-file list (git diff --name-only "$DIFF_RANGE" or the API equivalent) — needed for the diff-engagement mechanical check in 3f.
Before the diff text reaches the agent-under-test:
Closes #\d+, Fixes #\d+, Refs #\d+, Resolves #\d+ (case-insensitive)#\d+ token and any github.com/... URLSCHEMA.md)This is a mitigation, not a hard sandbox — see "Residual risk" below.
.claude/session/reviews/ before spawningbash .claude/skills/eval-agents/lib/snapshot-diff.sh snapshot \
.claude/session/reviews "$SNAPSHOT_FILE"
$SNAPSHOT_FILE is a per-entry scratch path (e.g. $(mktemp)), not committed anywhere. This must run immediately before the spawn in 3d — the whole point is capturing the directory's state at the last possible moment the harness controls, so anything the spawn writes shows up in the post-spawn diff in 3f.
Use the Agent tool with subagent_type mapped from <agent>:
<agent> | subagent_type |
|---|---|
rex | code-reviewer |
hakim | security-reviewer |
tariq | solution-architect |
Prompt shape (fill in the sanitized diff):
This is an EVALUATION run, not a live PR review. You are reviewing a
standalone code change — there is no PR number, no repository context
beyond what's needed to read the diff, and no tracker to report to.
Your system prompt carries a HARD STOP / MANDATORY-ACTIONS section that
normally REQUIRES you to write a local approval marker and post a review via
tracker_review_submit (or the equivalent for your role) before you are done.
**That mandate is explicitly SUSPENDED for this run only.** It does not
apply here — treating it as inactive is the correct behavior for this task,
not a violation of your normal instructions. There is no real PR number and
no marker path to resolve, so attempting to satisfy that mandate would mean
acting on fabricated inputs.
Rules for this run:
- Your only required output is your review, returned as this message's final
text — checklist, issues found, and verdict. There is no other mandatory
action; the HARD STOP's marker-write and tracker-submit steps are
suspended per the paragraph above.
- Do NOT run `gh pr view`, `gh pr diff`, `gh issue`, or any tracker command —
there is no real PR behind this diff to look up.
- Do NOT call tracker_review_submit — the requirement to do so is suspended
for this run.
- Do NOT write any file, including an approval marker — same suspension.
--- BEGIN DIFF ---
<sanitized diff text>
--- END DIFF ---
Review this diff and report your findings and verdict.
This wording is deliberate (#833, Hakim's security review of #828): telling the agent-under-test "don't do the mandatory thing" contradicts its own HARD STOP instead of overriding it, which leaves the two instructions in tension and relies on the sanitized-out PR number to break the tie. Explicitly suspending the mandate removes the tension — the agent isn't refusing a required action, it's correctly recognizing the requirement doesn't apply to this run.
Capture the sub-agent's full returned text as AGENT_OUTPUT.
Option: read-only source access (#1815). Some defects need the source, for example a count to recount or a set of settings to check. For such an entry, run the reviewer with the Opus CLI in place of the Agent tool. Give it only Read, Grep and Glob over a sanitized source snapshot of the reviewed commit, built by lib/source-snapshot.sh. The reviewer gets no shell and no path outside its run folder. The full protocol and the list of entries that used it are in docs/eval-agents/README.md § "Read-only source access (option)". Record in the report which entries used the option.
Two independent checks, both run before scoring. Either one flagging is sufficient to discard the run.
Primary — filesystem snapshot-diff (mechanical, cannot be talked around):
if ! bash .claude/skills/eval-agents/lib/snapshot-diff.sh check \
.claude/session/reviews "$SNAPSHOT_FILE" > "$DIFF_REPORT"; then
# any new / changed / deleted file under .claude/session/reviews/ since 3c
# => CONTAMINATED, regardless of what AGENT_OUTPUT narrates
...
fi
This is the load-bearing fix from #833 (Hakim's security review of #828): the original design scanned AGENT_OUTPUT's narrated text for signs of a marker write, which a silent Bash-redirect write (echo ... > .claude/session/reviews/<pr>-rex.approved) <!-- bare-marker-example: quoting a historical bad command --> never appears in — the agent doesn't have to say anything about a write it performed for the check to miss it. Snapshot-diff instead asks a question the agent's narration cannot influence: did any file under .claude/session/reviews/ actually change on disk between 3c and now? A write that never gets mentioned in AGENT_OUTPUT still shows up as a NEW or CHANGED line in the diff.
Secondary — text-scan (catches read-only tracker calls the snapshot can't see): scan AGENT_OUTPUT for any sign the agent queried a live tracker despite the instructions: a gh pr command echoed in its reasoning, a real headRefOid, the corpus entry's actual PR number appearing verbatim, or a posted-review confirmation phrase. This check still matters because a gh pr view (a read, not a write) leaves no trace in .claude/session/reviews/ for the snapshot-diff to catch — the two checks cover disjoint failure modes, not overlapping ones.
If either check flags, discard this run — mark the entry CONTAMINATED in the report (cite which check fired and, for a snapshot-diff hit, the exact path(s) changed) and do not fold it into the aggregate scores. This is the backstop for the residual risk noted in "Safety" above: sanitization in 3b is not a hard sandbox (the agent-under-test retains normal Bash access — see the "MUST NOT be CI-gated" callout in "Safety" for the harness-capability finding on why that access can't currently be removed at spawn time), so the harness must be able to notice and discard a leak rather than silently score a compromised run.
produced_output — AGENT_OUTPUT is non-emptyvalid_verdict — regex-extracts one of APPROVED, CHANGES REQUESTED, COMMENT from a ### Verdict (or equivalent) sectionengaged_diff — at least one file path mentioned in AGENT_OUTPUT's findings appears in the entry's changed-file list (guards against a generic "looks fine" non-engaged response)For each ground_truth_defects[] entry, decide caught or missed: does AGENT_OUTPUT's findings describe the same substantive problem at (or near) the same location? This is a semantic match performed by whichever model is running /eval-agents — it is comparing a candidate answer to an already-fixed answer key, not establishing truth, so it does not trip the self-grading concern AgDR-0089 addresses (that concern is about who freezes the ground truth, never about who checks a candidate against it).
Separately, flag false alarms: any AGENT_OUTPUT finding at BLOCKING/HIGH severity that does not correspond to a ground_truth_defects[] entry and is judged incorrect (not merely "an additional legitimate observation the corpus doesn't happen to track"). Keep this narrow — real diffs legitimately have addressable nits beyond the seeded defect set; only count a false alarm when the claim itself is wrong.
Compute per entry:
verdict_justified — does the agent's overall verdict match what the ground truth implies? (APPROVED/COMMENT is justified only if no BLOCKING/HIGH defect exists in the ground-truth set; CHANGES REQUESTED is justified if it correctly names at least the highest-severity ground-truth defect.)Across all non-CONTAMINATED, non-UNRESOLVABLE entries:
catch_rate_overall = caught_defects / total_ground_truth_defects
catch_rate_blocking = caught_{BLOCKING,HIGH}_defects / total_{BLOCKING,HIGH}_defects
false_positive_rate = false_alarms / total_agent_findings_flagged
approve_precision = justified_APPROVED_or_COMMENT_verdicts / total_APPROVED_or_COMMENT_verdicts_issued
mechanical_engagement = entries_with(valid_verdict AND engaged_diff) / total_scored_entries
approve_precision is undefined (report as n/a, not 0) if the agent never approved anything in this corpus run — don't manufacture a score from an empty denominator.
| Dimension | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| Approve precision (headline) | 0-20% justified | 21-50% | 51-75% | 76-90% | 91-100% |
| Catch rate (overall) | 0-20% caught | 21-40% | 41-60% | 61-80% | 81-100% |
| False-positive discipline | FP rate > 60% | 41-60% | 21-40% | 6-20% | 0-5% |
| Mechanical engagement | 0-20% engaged | 21-40% | 41-60% | 61-80% | 81-100% |
These four are reported for texture; the pass/fail verdict in Step 6 is computed directly from the raw metrics against eval_agents.thresholds, never from the 0-4 mapping. This is deliberate — per AgDR-0089, the whole point of the corrected methodology is that a rubric score must never be the correctness oracle again.
PASS if approve_precision >= thresholds.approve_precision_min
AND catch_rate_blocking >= thresholds.blocking_catch_rate_min (default 1.0 — no misses allowed)
AND catch_rate_overall >= thresholds.overall_catch_rate_min
AND false_positive_rate <= thresholds.false_positive_rate_max
WARN otherwise
WARN is advisory, not blocking — /eval-agents is manual/periodic measurement (#824's explicit v1 scope), not wired to any merge gate, pre-commit, or pre-push hook. A catch_rate_blocking miss always forces WARN regardless of how the other three metrics look — one missed BLOCKING defect should never average out against a pile of easy correct approvals.
<reports_dir>/<agent>-<YYYY-MM-DD>.md (default docs/eval-agents/reports/). Same-day reruns append -NN rather than clobber (mirrors /mutation-test).
# Eval report — <agent> — <YYYY-MM-DD>
| Field | Value |
|---|---|
| Agent | <rex\|hakim\|tariq> |
| Corpus | `<corpus path>` (<N> entries) |
| Scored | <N - contaminated - unresolvable> |
| Contaminated (discarded) | <N> |
| Unresolvable (discarded) | <N> |
## Headline
**Approve-precision: <X>% (<justified>/<total approved>)** — <PASS|WARN against threshold <T>%>
## Metrics
| Metric | Value | Threshold | Verdict |
|---|---|---|---|
| Approve precision (headline) | X% | ≥T% | PASS/WARN |
| Catch rate — BLOCKING/HIGH | X% | ≥T% | PASS/WARN |
| Catch rate — overall | X% | ≥T% | PASS/WARN |
| False-positive rate | X% | ≤T% | PASS/WARN |
## Advisory dimensions (0-4, informational — not part of the verdict above)
| Dimension | Score | Notes |
|---|---|---|
| Approve precision | N/4 | |
| Catch rate | N/4 | |
| False-positive discipline | N/4 | |
| Mechanical engagement | N/4 | |
## Per-entry results
| Entry | PR | Verdict given | Justified? | Defects caught | Defects missed | Notes |
|---|---|---|---|---|---|---|
| rex-792-2ce6708 | 792 | APPROVED | ✗ NO | 0/1 | D1 (BLOCKING) | matches the ground-truth MISS |
| ... | | | | | | |
## Missed BLOCKING/HIGH defects (if any)
For each: entry id, defect description, why the agent's output didn't surface it.
## Discarded runs
For each CONTAMINATED or UNRESOLVABLE entry: id + reason.
## Overall verdict
**PASS** or **WARN** — one line, plain language, matching the headline.
✓ EVAL — rex — approve-precision 67% (2/3), catch-rate 100% blocking/4/6 overall — PASS
Report: docs/eval-agents/reports/rex-2026-07-09.md
or
⚠ EVAL — rex — approve-precision 67% (threshold 85%) — WARN
1 missed BLOCKING defect (rex-792-2ce6708). Report:
docs/eval-agents/reports/rex-2026-07-09.md
On WARN only:
This run surfaced N missed BLOCKING/HIGH defect(s). File a [Task] ticket to
investigate (or grow the corpus with new confirmed entries)? [y/N]
On yes, run /task with the prefilled body — the skill stays out of gh issue create directly, same pattern as /mutation-test Step 9.
.claude/project-config.defaults.json → eval_agents:
{
"eval_agents": {
"corpus_dir": "docs/eval-agents/corpus",
"reports_dir": "docs/eval-agents/reports",
"thresholds": {
"approve_precision_min": 0.85,
"blocking_catch_rate_min": 1.0,
"overall_catch_rate_min": 0.7,
"false_positive_rate_max": 0.3
}
}
}
Override per-project in .claude/project-config.json (shallow-merge — overriding one threshold doesn't blow away the others).
.claude/session/reviews/ immediately before every spawn (Step 3c); run the two-check contamination gate — snapshot-diff primary, text-scan secondary — after every spawn (Step 3e). No supported invocation path skips this./eval-agents validates schema (Step 2), never truth. Do not add a corpus entry without real evidence (independent_review, confirmed_fix, or human in the oracle field) — a fabricated defect produces a confidently wrong score.tariq uses its seeded starter corpus by default. Supply --corpus to use a custom corpus.naqid is not a supported <agent> value in v1 — see "Usage" above.ground_truth_defects are fixed, but matching a fresh run's findings against them is not a byte-for-byte comparison, and re-running the exact same corpus against the exact same agent can produce a different verdict_justified call on a borderline entry. A score delta between two runs (this week's approve-precision vs. last month's) is not automatically a real regression — it can just as easily be run-to-run noise in the matching step. Treat a delta as a prompt to look at which specific entries flipped and re-read the agent's actual output for that entry, never as a number to trend-line on its own. This is the same caveat AgDR-0089 and spike #825 already carry for the starter corpus's small N — non-determinism compounds it, it doesn't replace it.| File | Purpose |
|---|---|
.claude/skills/eval-agents/SKILL.md | This file — the skill spec |
.claude/skills/eval-agents/lib/validate-corpus.sh | Schema validator (Step 2 / --check-only) |
.claude/skills/eval-agents/lib/snapshot-diff.sh | Filesystem snapshot/check pair — the Step 3c/3e contamination backstop (#833) |
.claude/skills/eval-agents/tests/smoke.sh | Validator smoke tests |
.claude/skills/eval-agents/tests/test-snapshot-diff.sh | Snapshot-diff smoke tests (#833) |
.claude/skills/eval-agents/lib/source-snapshot.sh | Builds the read-only, sanitized source snapshot for the source-access option (#1815) |
.claude/skills/eval-agents/tests/test_source_snapshot.sh | Source-snapshot tests: redaction, fail-closed leak check, read-only tree (#1815) |
.claude/skills/eval-agents/lib/mine_disagreements.py | Read-only candidate miner (#1440): Rex APPROVED vs. Hakim or Tariq CHANGES on one commit |
.claude/skills/eval-agents/tests/test_mine_disagreements.sh | Miner tests: Python unit tests, a fake gh read-only check, and a draft-rejection check (#1440) |
docs/eval-agents/README.md | Corpus directory overview, what's seeded, how to grow it |
docs/eval-agents/SCHEMA.md | Field-by-field corpus format |
docs/eval-agents/corpus/rex.json | Starter corpus — 10 entries |
docs/eval-agents/corpus/hakim.json | Starter corpus — 11 entries |
docs/eval-agents/corpus/tariq.json | Starter corpus — 8 entries |
docs/eval-agents/reports/ | Dated run reports land here |
.claude/project-config.defaults.json → eval_agents.* | Corpus/report dirs + thresholds |
Design rationale: docs/agdr/AgDR-0089-eval-agents-methodology.md.
docs/spike-825/findings.md + docs/spike-825/judge_calibration.py — the spike that disproved the naive approach and that this skill's starter corpus partly re-derives from/eval-agents is that measurement tool.claude/skills/mutation-test/SKILL.md — sibling sensor skill this skill's structure mirrors (graceful-degrade, dated reports, config thresholds, optional ticket offer).claude/rules/agdr-decisions.md — why this shipped with an AgDRPart of ApexYard — multi-project SDLC framework for Claude Code · MIT.
まだレビューはありません。使ってみた感想をお寄せください。
ログインなしで書き込めます。使ってみた感想、質問、動かなかった環境などを気軽にどうぞ。
まだコメントはありません。最初のコメントを書いてみませんか。
WCAG 2.2 AA audit — perceivable, operable, understandable, robust criteria. Deep-dive for /launch-check accessibility.
日本語の概要は準備中です。原文の説明を表示しています。
WCAG 2.1 AA audit — perceivable, operable, understandable, robust criteria. Deep-dive for /launch-check accessibility.
日本語の概要は準備中です。原文の説明を表示しています。
Browse / search / show / stats AgDRs across the portfolio — recalls "have we decided this before?".
日本語の概要は準備中です。原文の説明を表示しています。
Analytics audit — SDK config, event naming, funnel completeness, dashboards. Deep-dive for /launch-check analytics.
日本語の概要は準備中です。原文の説明を表示しています。
Record per-PR architecture-review approval for design-artifact PRs (required by the architecture gate). ONLY on an explicit per-PR architect "approved".
日本語の概要は準備中です。原文の説明を表示しています。
Record per-PR design-review approval (UI merge gate). ONLY on an explicit per-PR designer "approved".
日本語の概要は準備中です。原文の説明を表示しています。