DEV Community

Morgan Xu
Morgan Xu

Posted on

A New Open-Weight Model Drops Every Week. Here's the 30-Minute Eval Harness I Run Before Believing Any Benchmark

If your feed looks anything like mine this week, it's wall-to-wall hot takes about the latest open-weight coding model release — right now it's MiniMax's H3 getting the treatment, last month it was something else, next month it'll be something newer. The pattern is always the same: a launch post, a set of vendor benchmark tables, a wave of "this changes everything" threads, and then, quietly, a trickle of "actually it's mediocre at X" follow-ups.

I'm not going to tell you whether H3 is good. I haven't run it through enough of my own workloads to say, and honestly, neither have most of the people posting about it. What I can give you is something more durable: a small, reproducible evaluation harness that takes about 30 minutes to set up and tells you whether any newly released model is worth switching to for your code, not for a benchmark suite.

Why vendor benchmarks don't answer your actual question

The question a launch benchmark answers is: "How does this model perform on a standardized set of problems under controlled prompting?" The question you actually have is: "Will this model waste less of my time than the one I'm already using, on the kind of work I actually do?"

These diverge fast. Your work has a specific language mix, a specific repo style, specific tolerance for verbosity, specific failure modes you can't stand (mine: confidently inventing API methods that don't exist). A two-point swing on a public leaderboard says almost nothing about any of that.

So the harness below is built around your tasks, scored automatically where possible and by checklist where not.

The harness: 12 tasks, 3 scoring tiers

The whole thing is a directory of task prompts plus a runner script. Here's the structure:

model-eval/
├── tasks/
│   ├── 01_fix_failing_test.md
│   ├── 02_refactor_for_readability.md
│   ├── 03_explain_unfamiliar_code.md
│   ├── ...
│   └── 12_migration_snippet.md
├── runner.py
├── score.py
└── results/
Enter fullscreen mode Exit fullscreen mode

Each task file is a prompt plus an expected-output specification. Three tiers of tasks, four each:

Tier 1 — mechanically verifiable. Tasks where the output can be checked by a script: "fix this failing pytest," "write a function passing these assertions," "produce valid JSON matching this schema." These get scored by actually executing the output.

Tier 2 — checklist verifiable. "Refactor this function" tasks scored against an explicit rubric: no behavior change (run the existing tests), no new dependencies, cyclomatic complexity not increased. Partially automatable.

Tier 3 — judgment tasks. "Explain what this regex does," "review this diff for security issues." Scored by you, but against a written answer key you make before looking at model output, so you're not grading on vibes.

Here's the runner — deliberately boring, provider-agnostic, ~40 lines:

import json, subprocess, sys, time
from pathlib import Path

def run_task(client, model: str, task_file: Path) -> dict:
    prompt = task_file.read_text()
    start = time.time()
    response = client.complete(model=model, prompt=prompt, max_tokens=2048)
    elapsed = time.time() - start
    return {
        "task": task_file.stem,
        "model": model,
        "output": response.text,
        "latency_s": round(elapsed, 2),
        "tokens_out": response.usage.completion_tokens,
    }

def score_tier1(result: dict, task_dir: Path) -> bool:
    """Write model output to a file, run the task's checker script."""
    candidate = task_dir / "_candidate.py"
    candidate.write_text(result["output"])
    checker = task_dir / "check.sh"
    proc = subprocess.run(["bash", str(checker)], capture_output=True)
    return proc.returncode == 0

if __name__ == "__main__":
    model = sys.argv[1]
    client = get_client()  # whatever provider you're testing
    results = []
    for task in sorted(Path("tasks").glob("*.md")):
        r = run_task(client, model, task)
        r["tier1_pass"] = score_tier1(r, Path("tasks")) if is_tier1(task) else None
        results.append(r)
    Path(f"results/{model.replace('/', '_')}.json").write_text(
        json.dumps(results, indent=2)
    )
Enter fullscreen mode Exit fullscreen mode

Twelve tasks feels small. It is small — deliberately. You'll actually finish a 12-task eval for every new model that drops. You will not finish a 200-task eval more than once, and an eval you run once is a demo, not a tool.

Where to run it without torching your budget

The annoying part of doing this properly used to be access: to compare a new open-weight model against your incumbent, you need both models available through something you can script against, and paying per-token across several providers for eval runs adds up fast, especially when you're iterating on the harness itself.

This is where I've been using MonkeyCode. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The relevant bit for this workflow is that it offers free access to a set of models plus a free server option, which means the "spin up an endpoint, point the runner at it, tear it down" loop costs me nothing while I'm debugging the harness — and harness debugging is where most of the token spend actually goes, not the final eval run. I'd rather burn my mistakes on a free tier and reserve paid API calls for the comparisons that matter.

What I appreciate beyond the pricing is the posture: the project leans into open source — the tooling is out in the open, you can read what the client is actually sending, and you're not reverse-engineering a black box to figure out why your eval numbers look weird. For an eval harness specifically, that transparency matters more than for casual use, because "I don't know what the wrapper did to my prompt" invalidates your results.

If you want to try this workflow yourself, the free server option is enough to get the runner above working end-to-end; grab a task from your own recent git history and start there.

What I learned running this against recent releases

Without naming specific scores (your tasks will differ, and that's the whole point), the pattern across the last few open-weight releases I've put through this harness:

  1. Tier 1 gaps are smaller than marketing implies. Most current-generation open coding models pass the mechanically-verifiable tasks at similar rates. The differentiation has moved to Tier 2 and 3.
  2. Verbosity is a hidden cost. One model I tested passed tasks at the same rate as my incumbent but produced 40% more output tokens per task — more to review, more latency, more cost per accepted suggestion. My harness counts tokens_out for exactly this reason.
  3. Failure style matters more than failure rate. I keep a one-line note per failed task: "wrong but obviously wrong" vs "wrong and confident-looking." Models in the second category get rejected even with decent pass rates, because review time is my real bottleneck.
  4. Latency on your hardware/endpoint is not the benchmark's latency. A model that looks great in a datacenter demo can be unusable if the endpoint you're actually hitting is slow. Measure it yourself; it's two lines of code, as above.

Limitations, and who shouldn't bother

  • Twelve tasks can't measure everything. This harness is a smoke test, not a certification. It's good at answering "is this worth a week-long trial," bad at answering "is this safe for production code review."
  • Task selection bias is real. If your tasks are all Python bugfixes, you'll pick the model that's best at Python bugfixes. That's fine if that's your job; it's a trap if it isn't. Rebuild the task set when your work changes.
  • Judgment-tier scoring is still you. The pre-written answer key reduces but doesn't eliminate grader drift.
  • If you only use a model for autocomplete-style completions, this harness is overkill — you're better off measuring accept-rate in your editor for a week.
  • Free tiers change. Any free model access or free server option — MonkeyCode's included — is an availability claim, not a permanent guarantee. Design your harness so the client is swappable and you're never locked into one endpoint.

The actual takeaway

The next time a model drops and the feed explodes — H3 today, something else in three weeks — the correct response isn't hype or dismissal. It's: pull the release, run your twelve tasks, compare against your incumbent on your rubric, and make a boring evidence-based decision in an afternoon. The teams that get value from the current pace of model releases aren't the ones with the strongest opinions; they're the ones with the cheapest evaluation loop.

Build the harness once. Every future launch post becomes a 30-minute chore instead of a week of discourse.

Top comments (0)