skill-benchmarking — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited skill-benchmarking (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 1 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Strict, agent-agnostic benchmark runner for evals.json skill evaluation. Produces benchmark-<model>.json with pass rates and a discriminating assertion list. Only assertions that actually discriminate between with-skill and without-skill responses are kept; non-discriminating noise is removed via the assertion hygiene process.
This skill works with any AI coding assistant -- Claude Code, Gemini CLI, GitHub Copilot, Cursor, Windsurf, or any agent that can read files and run shell commands.
If you are using Gemini CLI, GitHub Copilot, Cursor, or another AI coding assistant:
scripts/benchmarking/SKILL.md) -- it is the complete workflow guide| Claude Code feature | Equivalent for other agents |
|---|---|
Explore subagent | Start a fresh chat session, or use your agent's subprocess/tool-use feature |
Read / Write tools | Use your agent's file read/write commands, or cat / shell redirects |
Bash tool | Use your agent's terminal/shell execution capability |
All Python scripts are standalone and require only Python 3.10+. No external dependencies.
Violating any of these means the benchmark is invalid. Start over.
| Task | Tool | Why |
|---|---|---|
Create workspace dirs + eval_metadata.json | scaffold.py | Deterministic file layout |
Write response.md from a pre-existing batch outputs JSON | unpack-outputs.py | Deterministic file layout |
| Grade responses against assertions | AI (separate session) | Requires language understanding |
Write grading.json files from batch AI grading output | ingest-grades.py | Deterministic file layout |
Produce benchmark-<model>.json | aggregate.py | Pure arithmetic |
| Review results visually | eval-viewer/generate_review.py | Interactive HTML viewer |
| Analyze benchmark patterns | AI with `references/analyzer-prompt.md` | Pattern recognition |
| Blind comparison of outputs | AI with `references/comparator-prompt.md` | Quality judgment |
| Update README benchmark tables | AI (main context) | README structure is flexible, not strict |
| Improve skill based on failed assertions | AI (main context) | Requires judgment about what to generalise |
All scripts live in scripts/benchmarking/ relative to the repository root.
Phase 1: Scaffold -> create iteration dir + eval_metadata.json [script]
Phase 2: Generate -> produce with_skill and without_skill responses [AI or unpack-outputs]
Phase 3: Grade -> isolated session grades responses [AI, separate context]
Phase 4: Aggregate -> produce benchmark-<model>.json [script]
Phase 4a: View -> review results in interactive HTML viewer [script, optional]
Phase 4b: Cleanup -> remove non-discriminating assertions, re-grade [AI + script]
Phase 5: README -> update skill README with new benchmark results [AI]
Phase 6: Improve -> add missing content to skill files [AI]For a new benchmark: run all phases. For a re-grade only: run Phases 3-4. For adding a new model to an existing iteration: run Phases 2-4 (scaffold already done, metadata exists). For README + skill update only: run Phases 5-6 after a completed benchmark.
python scripts/benchmarking/scaffold.py \
skills/<platform>/<skill-name> \
workspaces/<platform>/<skill-name>/iteration-N \
<model-slug>Auto-detect next iteration N by listing existing iteration-* dirs and incrementing. Creates eval-<name>/eval_metadata.json and empty output dirs for <model-slug>-with and <model-slug>-without, with 3 run slots (run-1/, run-2/, run-3/) per variant by default. Use --runs 1 to scaffold only one run.
For each eval in evals.json, produce two responses in the same model, same settings. Never use a stronger/different model for with_skill vs without_skill.
with_skill prompt:
Read the following skill file and every reference file it mentions:
skills/<platform>/<skill-name>/SKILL.md
Then answer this question. Save your complete response (no preamble) to:
workspaces/<platform>/<skill-name>/iteration-N/eval-<name>/<model-slug>-with/run-1/outputs/response.md
Question: <eval.prompt>without_skill prompt:
Answer this question. Do NOT read any skill or reference files.
Save your complete response (no preamble) to:
workspaces/<platform>/<skill-name>/iteration-N/eval-<name>/<model-slug>-without/run-1/outputs/response.md
Question: <eval.prompt>If responses were already generated and stored in a standard batch file:
{
"model": "<model-slug>",
"skill": "<skill-name>",
"outputs": [
{
"eval_name": "queue-creation-simple",
"response_with_skill": "...",
"response_without_skill": "..."
}
]
}python scripts/benchmarking/unpack-outputs.py \
evals/<platform>/<skill-name>/<model-slug>-outputs.json \
workspaces/<platform>/<skill-name>/iteration-NGrade in a separate context that has NOT read the skill being tested.
Explore subagentgemini in a new terminal)Pass ONLY:
response.md contents for one variant (with OR without -- not both)eval_metadata.jsonreferences/grading-rules.mdDo NOT pass: SKILL.md, any skill reference files, or any description of what the skill teaches. Grade with and without variants in separate grading sessions.
Copy this prompt into the fresh grading session:
You are a strict evaluator. Your only job is to grade responses against assertions.
## Grading Rules
<full contents of scripts/benchmarking/references/grading-rules.md>
## Your Task
Grade each response below. Evidence-only. No charity. No benefit of the doubt.
- PASS: include a short direct quote from the response.
- FAIL: state exactly what was missing.
Return a single JSON array, no preamble, no explanation:
[
{
"eval_id": <number>,
"variant": "SET_A",
"eval_name": "<name>",
"assertions": [{"id": "X1.1", "passed": true|false, "notes": "..."}],
"summary": {"passed": N, "failed": N, "total": N, "pass_rate": 0.XX}
}
]
## Evals to Grade
### Eval <id>: <eval_name>
**Assertions:** [...]
**Response:**
<response content>
---
<repeat for each eval>After receiving the JSON array, save it to /tmp/grades_SET_A_run1.json and ingest:
python scripts/benchmarking/ingest-grades.py \
/tmp/grades_SET_A_run1.json \
workspaces/<platform>/<skill-name>/iteration-N \
<model-slug> \
--run 1Repeat the grade+ingest cycle for --run 2 and --run 3 (each run gets an independent response and grading). Repeat for the other variant (SET_B = without_skill).
aggregate.py auto-detects all run-* dirs and averages pass_rates across runs.
python scripts/benchmarking/aggregate.py \
workspaces/<platform>/<skill-name>/iteration-N \
<model-slug>Reads all eval-*/eval_metadata.json and <model-slug>-{with,without}/run-1/grading.json. Writes benchmark-<model-slug>.json to iteration-N/.
If the script reports missing grading files, grade the missing evals first.
After aggregation, launch an interactive HTML viewer to review all responses, grading results, and benchmark data side-by-side.
python scripts/benchmarking/eval-viewer/generate_review.py \
workspaces/<platform>/<skill-name>/iteration-N \
--skill-name <skill-name>This starts a local web server (default port 3117) and opens the viewer in your browser. The viewer:
feedback.json in the workspace# Compare with a previous iteration
python scripts/benchmarking/eval-viewer/generate_review.py \
workspaces/<platform>/<skill-name>/iteration-2 \
--previous-workspace workspaces/<platform>/<skill-name>/iteration-1
# Include benchmark data in the viewer
python scripts/benchmarking/eval-viewer/generate_review.py \
workspaces/<platform>/<skill-name>/iteration-N \
--benchmark workspaces/<platform>/<skill-name>/iteration-N/benchmark-<model-slug>.json
# Generate a static HTML file instead of starting a server
python scripts/benchmarking/eval-viewer/generate_review.py \
workspaces/<platform>/<skill-name>/iteration-N \
--static /tmp/review.htmlAfter benchmarking, review assertions for hygiene. The goal is to keep only assertions that genuinely discriminate between skill-guided and unguided responses.
evals.json.evals.json, re-run Phases 3-4 with the updated assertions. This makes the reported delta honest and closer to truth.This step is critical for iteration-over-iteration comparisons. Without it, inflated pass rates from non-discriminating assertions mask real skill gaps.
Read skills/<platform>/<skill-name>/README.md and the new benchmark-<model-slug>.json. Add to the README:
#### Results (<Display Name>) section matching the format of existing rows#### Key Discriminating Assertions (<Display Name>) section listing top missesDo not reformat or rewrite existing content -- only add new content in the established structure.
Binary pass/fail benchmarks cannot distinguish a response that barely mentions a concept from one that provides structured explanation with code and rationale. Run a blind A/B comparison to capture quality differences.
For the full blind comparator methodology and output format, see references/comparator-prompt.md.
A blind judge reads both responses (labelled A and B), scores each 0-10, and declares a winner -- without knowing which response used the skill. This captures structure, code examples, precision, completeness, and actionability differences invisible to binary assertions.
Alternate a_is assignment across evals to prevent position bias:
a_is = "with"a_is = "without"You are a blind quality judge for technical responses. Compare pairs of responses (A and B).
For each eval:
1. Read BOTH response files
2. Score each 0-10 (half-point ok). Criteria: structure, code examples, precision, completeness, actionability.
3. Declare winner: "A", "B", or "tie" (tie only if scores differ by <= 0.5)
4. Write a JSON result file to the output path
**IMPORTANT: You do NOT know which response used the skill. Stay objective.**
## Eval List
Base path: <iteration-dir>
Model: <model-slug>
| # | eval_name | a_is | A-response path | B-response path | output path |
|---|-----------|------|-----------------|-----------------|-------------|
| 1 | <name> | with | eval-<name>/<model>-with/run-1/outputs/response.md | eval-<name>/<model>-without/run-1/outputs/response.md | eval-<name>/ab-<model>.json |
| 2 | <name> | without | eval-<name>/<model>-without/run-1/outputs/response.md | eval-<name>/<model>-with/run-1/outputs/response.md | eval-<name>/ab-<model>.json |
...
## Output JSON Format (per eval)
{
"eval_name": "<name>",
"a_is": "with|without",
"winner": "A|B|tie",
"with_skill_won": true|false|null,
"scores": {"A": 8.5, "B": 7.0},
"reasoning": "one sentence distinguishing the responses"
}
with_skill_won rules:
- a_is=with & winner=A -> true
- a_is=with & winner=B -> false
- a_is=without & winner=B -> true
- a_is=without & winner=A -> false
- tie -> null
Final Report: Table + totals: with_skill wins: X/N, avg with_skill score: Y.Y, avg without_skill score: Z.ZRun in batches of 8-12 evals per session to keep context manageable. For 24 evals, use 2-3 batches.
import json, glob
files = glob.glob("workspaces/<platform>/<skill>/iteration-N/eval-*/ab-<model>.json")
results = [json.load(open(f)) for f in files]
wins = sum(1 for r in results if r["with_skill_won"] is True)
ties = sum(1 for r in results if r["with_skill_won"] is None)
losses = sum(1 for r in results if r["with_skill_won"] is False)
avg_with = sum(r["scores"]["A"] if r["a_is"]=="with" else r["scores"]["B"] for r in results) / len(results)
avg_without = sum(r["scores"]["B"] if r["a_is"]=="with" else r["scores"]["A"] for r in results) / len(results)
print(f"{wins}/{len(results)} wins, {ties} ties, {losses} losses | avg {avg_with:.1f} vs {avg_without:.1f}")In the Results Summary table, add an A/B Quality column:
| Model | With Skill | Without Skill | Delta | A/B Quality |
|-----------|-----------|---------------|-----------|------------------------------------------|
| GPT-5.4 | 100% | 82.2% | **+17.8%**| **20/24 wins**, 4 ties (avg 8.5 vs 7.4) |In the root README skills table, append to each model delta cell: **+17.8%** . 20/24 A/B (8.5 vs 7.4)
Add this note below the root README table:
A/B column format: wins/total A/B (avg with vs avg without) -- a blind judge scores both responses 0-10 and picks the better one without knowing which used the skill; position is randomized each eval.After A/B comparison, run the analyzer to understand WHY the winner won and generate improvement suggestions.
Use the prompt template in references/analyzer-prompt.md. The analyzer:
This is most useful when comparing two versions of the same skill across iterations.
If discriminating assertions fail with the skill, the skill under-specifies that concept.
discriminating_assertions_failed_by_baseline from the benchmark JSONiteration-N+1 to confirm improvementIf you cannot create a separate context for grading:
ingest-grades.py to write the individual grading filesCritical: Never grade in the same context where you read the skill's SKILL.md or references. This contaminates the grading and invalidates results.
For agents that support it, you can also:
grade_responses.py script for basic keyword/pattern-based grading (flags semantic assertions for manual review)grader-prompt.md template for a structured grading session| File | Purpose |
|---|---|
scaffold.py | Create iteration workspace from evals.json |
unpack-outputs.py | Write response.md files from a pre-existing batch outputs JSON |
ingest-grades.py | Write grading.json files from a batch AI grading response |
aggregate.py | Aggregate grading artifacts into benchmark-<model>.json |
aggregate_benchmark.py | Alternative aggregator with stddev/min/max statistics |
grade_responses.py | Programmatic keyword/pattern grading (flags semantic checks for AI review) |
references/grading-rules.md | Strict pass/fail contract for graders |
references/grader-prompt.md | Structured grading prompt template for any AI agent |
references/analyzer-prompt.md | Post-hoc analysis prompt for understanding A/B comparison results |
references/comparator-prompt.md | Blind comparator prompt for A/B quality judgment |
eval-viewer/generate_review.py | Generate and serve interactive HTML eval reviewer |
eval-viewer/viewer.html | HTML template for the eval viewer |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.