Chapter 07 · Dreambase Skill Creator
Subchapter 7.1
references/eval-loop.mdMarkdown7 KBView on GitHub
How to test, benchmark, and iterate on a skill in this repo. Read this before running any skill evals.
Committed with the skill:
skills/dreambase-<name>/evals/evals.json # prompts + assertionsGenerated during runs (gitignored via *-workspace/):
skills/dreambase-<name>-workspace/
├── skill-snapshot/ # copy of the skill before edits (when improving)
└── iteration-1/
├── eval-<id>-<slug>/
│ ├── eval_metadata.json
│ ├── with_skill/outputs/ + timing.json + grading.json
│ └── without_skill/outputs/ + ... # or old_skill/ when improving
├── benchmark.json
└── benchmark.mdThe workspace sits next to the skill directory, never inside it — nothing generated during testing ships with the skill.
{
"skill_name": "dreambase-example",
"evals": [
{
"id": 1,
"prompt": "Realistic user prompt, written the way a real user types",
"expected_output": "Plain-language description of a good result",
"files": ["fixtures/input.csv"],
"assertions": [
"Output CSV has a profit_margin column",
"All rows from the input are preserved"
]
}
]
}files are fixture inputs, committed under evals/fixtures/ if small; regenerate or document how to fetch if large.Spawn all runs in one turn — for each eval, two parallel subagents (they finish together; sequencing wastes wall-clock and lets conditions drift):
.../eval-<id>/with_skill/outputs/). Tell it exactly which artifacts to save.without_skill/outputs/.cp -r skills/dreambase-<name> <workspace>/skill-snapshot/), point the baseline at the snapshot → old_skill/outputs/.Write eval_metadata.json in each eval directory (eval_id, descriptive eval_name, prompt, assertions — empty array is fine at spawn time).
While runs execute, draft assertions (below) — don’t idle.
As each run completes, capture its timing from the task notification into timing.json (total_tokens, duration_ms) immediately; that data isn’t persisted anywhere else.
Good assertions are objectively checkable and named so a human scanning results understands each one instantly:
Prefer a small script over eyeballing when an assertion is mechanically checkable (file exists, column present, schema valid) — scripts are reusable across iterations and don’t drift. Skills with inherently subjective output (tone, design) get few or no assertions; human review carries the weight there.
For each run, evaluate every assertion against the outputs and write grading.json:
{
"expectations": [
{
"text": "Output CSV has a profit_margin column",
"passed": true,
"evidence": "Column present at index 5 with numeric values in all 42 rows"
}
]
}Use exactly the field names text, passed, evidence — downstream tooling depends on them. Aggregate pass rates, tokens, and time across configurations into benchmark.json/benchmark.md, then put results in front of the user for qualitative review. Watch for:
The skill will run across thousands of varied prompts; you iterate on three. So:
scripts/ and reference it.Then rerun everything into iteration-<N+1>/ (fresh baselines included) and compare. Stop when feedback comes back clean or improvements plateau.
Do this last, once the body is stable — a great skill that never triggers is worthless, and one that triggers everywhere is noise.
[
{"query": "hey can you pull last week's campaign numbers into a summary for my standup", "should_trigger": true},
{"query": "summarize this PDF my accountant sent me", "should_trigger": false}
]If working inside Claude Code with the built-in skill-creator skill available, its scripts/run_loop.py automates this step — prefer it over hand-rolling.