Skip to content

Configuration

skill-lens.toml is optional. It is located via --config, or otherwise discovered by searching upward from the current directory — the repo root is the conventional home, not a requirement.

default_runner = "fake"
min_pass_rate = 1.0
fail_on_error = true

[per_skill_min]
greeting = 0.9
Key Default CLI override
default_runner "fake" --runner
model "openai:gpt-4o-mini" --model
temperature 0.0
retries 2
retry_backoff_seconds 1.0
judge "fake"
judge_model "" (falls back to model) --judge-model
judge_temperature 0.0
min_pass_rate 1.0 --min-pass-rate
fail_on_error true
per_skill_min {}
baseline "" --baseline
repeat 1 --repeat
min_delta unset --min-delta
concurrency 1 --concurrency

Resolution order is CLI flag > config file > built-in default. API keys come from environment variables only and are never read from config.

baseline is "" (off), "none" (compare against an empty skill) or "previous" (compare against the prior version resolved from git). repeat is how many times each arm is sampled per case. min_delta has no default — leaving it unset means the delta is reported but not gated, and 0.0 is a real, stricter choice ("must not regress") rather than the same as unset. All three are detailed in Comparative evals, including why min_delta requires baseline to be set.

concurrency bounds how many cases run at once. It defaults to 1, which runs everything sequentially and behaves exactly as it did before the option existed. The work is network-bound — one provider round trip per case against sub-millisecond of local work — so raising it overlaps waiting, not computation; the practical ceiling is your provider's rate limit, not your CPU. Runners and evaluators are shared across threads, so a custom one must have no mutable state its run/evaluate touches.

model, retries, and retry_backoff_seconds only matter to components that reach a provider (pydantic-ai, as a runner or a judge); FakeRunner and FakeJudge ignore them. temperature accepts a float or the literal string "unset", for reasoning models that reject any explicit temperature:

default_runner = "pydantic-ai"
model = "openai:gpt-4o-mini"
temperature = 0.0            # or "unset" for reasoning models, which reject it
retries = 2
retry_backoff_seconds = 1.0

A blank model id is rejected as a user error (exit 2) rather than being passed to a provider, whether it arrives from model, judge_model, or the matching flag.

Judging

judge selects the judge the same way default_runner selects the runner, and defaults to "fake" for the same reason: upgrading must never start spending money on its own.

judge = "pydantic-ai"
judge_model = ""             # empty falls back to `model`
judge_temperature = 0.0      # or "unset" for a reasoning judge model

The default judge = "fake" does not grade. Rather than passing a rubric it never checked, it reports the case as errored — nothing was verified, so nothing is reported as verified. A consequence worth knowing: --judge-model does nothing on its own, because the judge is selected by judge, not by naming a model.

judge_temperature is deliberately separate from temperature and defaults to 0. Sampling the skill under test is a normal thing to want; sampling the grader is not, because an unstable judge makes the same output pass one day and fail the next, which is indistinguishable from a real regression.