Why Satyrn has its own evals
Satyrn's charter is to keep a small model on track, so a Python developer can use local AI and stay at the wheel. Every part of the eval harness follows from that sentence. This page says how, cites the parts of Satyrn Evals that carry it, and compares one well-known framework, Harbor, as an example.
The model on the developer's machine
Public agent benchmarks are aimed at hosted frontier models: large models behind an API, run at scale in cloud sandboxes, ranked on a leaderboard. The question they answer is "how capable is this model?"
Satyrn asks a different question: can a 9B model running on a laptop finish a real Python task, and if not, what would help it? That changes what has to be measured and controlled.
- The model is served locally, and its settings are part of the experiment. Release two ran Ornith 1.5 9B in MLX 8-bit through oMLX. Each run record freezes the model, sampling, context, and per-turn cap, verified against the server and client configuration rather than declared (the numbers, "What was compared").
- Budgets are tokens and turns, not wall-clock time. A frozen record names
token_budget,turn_budget, and acommand_backstop_sthat exists only to catch a hung machine. On a shared laptop, wall-clock time measures the machine; tokens and turns measure the model. - Throughput is a measured constraint. Running three cells at once was
adopted only after measuring it on live completions
(
evidence/2026-09-15-finishing-counterfactual/run-2/q3stats.md). - Small-model failure modes need their own instruments. A 9B model
produces failures a frontier model rarely does: a turn that runs to the cap
with no tool call, repeated identical edits, searching the filesystem for the
grader. The census has a
runawayclass for the first, and remediations records the rest, each with the detector that found it.
Evidence fine enough to choose a remedy
A pass rate says whether something works. It does not say what to build next. Satyrn exists to build remedies for small models, and a remedy has to be aimed at a cause. The project learned this the hard way: "We built the remedy for the failures we saw, and the failures we saw were the harness's" (lessons). So the harness is built to produce evidence at the level a remedy is designed against.
- Every attempt keeps its evidence. The patch and transcript are saved
before grading, and grading reads only what was saved. A grading fix is
re-applied to retained cells with
rescore, with no new model run (src/satyrn_evals/rescore.py). - Verdicts are per test. The hidden suite runs under a pytest plugin,
-p satyrn_evals.oracle_hook, which writes one outcome per test ID. Each task's manifest names theexpected_test_idsit must see, so a missing test is a finding, not a silent pass. - A run can be read at a declared line. A record's
line_token_budgetandline_turn_budgetsnapshot a cell's patch at the transcript line that crosses either one, andsatyrn-evals grade-linegrades those snapshots offline (src/satyrn_evals/line_grade.py). A cell given 48,000 tokens can still answer "had it passed by 32,000?" - Failures are classified before anything is built. The release-two census
classified every Baseline cell by why it failed: information, ambiguity,
capability, budget, finishing, runaway
(
evidence/2026-09-16-census/classes-summary.md). The finishing class, where a cell reached a passing state and kept going, is what the Engine's finish-on-green behaviour targets. - A remedy is estimated offline before it is built. The finishing
counterfactual replayed retained cells to ask how many a stop-at-green rule
would have rescued (
evidence/2026-09-15-finishing-counterfactual/). - Events are counted from the transcript. Tool calls are counted from
tool_execution_start, one per call, so a cost figure means the same thing in both arms.
A harness a Python developer already knows
The developer Satyrn serves writes specs and tests, not agent infrastructure. The harness is built from the tools that developer already uses, so reading a task, a grader, or a result needs nothing new.
- A task is a Python repository. Each task under
src/satyrn_evals/tasks/has abase/tree with apyproject.tomlanduv.lock, amanifest.json, anoverlay/of hidden tests, and two fixtures. - The prompt reads like a plan step. The
selfhost-run-record-gatecontract says which files to create, which interfaces to produce, and to write failing tests first, then implement, then runuv run pytest. - The grader is pytest. The hidden suite is ordinary test files; the
public suite is
uv run pytest -q. A known-good and a known-broken patch prove each grader in both directions. - Results are diffs and JSON. A cell's output is a patch you can apply and
a transcript you can read. A run record is one JSON file in
records/. - No containers. The model runs as a second local user,
satyrn-cell, with no view of grader material, and preflight checks the workspace before any cell starts. The checks run throughjust gates. - The default test tier runs offline. No model, network, or subprocess;
the tripwire in
tests/conftest.pyenforces it.
An example: Harbor
Harbor, from
the team behind Terminal-Bench, is a framework for running agent evaluations at
scale. Its task format (task.toml schema 1.4, read 2026-10-03) is close to
Satyrn's in structure, and the two projects reached several of the same
decisions on their own.
| Harbor | Satyrn Evals | |
|---|---|---|
| prompt | instruction.md |
manifest contract, with prompt rungs in contracts |
| environment | environment/Dockerfile, compose, or Apptainer |
a base/ Git tree at a recorded base_sha, run as satyrn-cell |
| hidden tests | tests/, copied in after the agent finishes |
overlay/, proven absent from the workspace |
| verdict | test.sh writes reward.txt or reward.json |
the pytest hook writes one outcome per expected test ID |
| reference solution | solution/solve.sh |
fixtures/known-good.patch, plus known-broken.patch |
| budget | [agent].timeout_sec |
token_budget, turn_budget, a per-turn cap |
| when grading happens | in the live container, after the run | offline, from the saved patch and transcript |
| run definition | job config: agents × tasks × attempts | frozen record: also stop_rule, decision_rule, task_tree_sha256 |
| trace | ATIF trajectory | Pi transcript, counted from tool_execution_start |
| where it runs | Docker locally, or cloud sandboxes | one laptop, local model server |
Where they agree. The verdict comes from a file the tests write, not from stdout or an exit code. Grader tests are hidden from the agent. Every task ships a reference solution that shows it can be solved.
Where Harbor fits better. Comparing many agents and models across established benchmarks, multi-container environments, cloud sandboxes, and a shared task registry with adapters for existing datasets.
Where Satyrn needs something else. Grading saved evidence, so a cell can be re-read at a budget line or re-scored after a grader fix; a grader proven against a known-broken patch; token and turn budgets for a local model; and run records that freeze the decision rule before the run.
The formats map closely enough that a Satyrn task could be exported as a
Harbor task, with the contract as instruction.md, the overlay and hook in
test.sh, and the known-good patch in solve.sh. That would let Satyrn's
tasks run against other agents; the evidence Satyrn needs would still come
from its own harness.