Evaluating Claude Opus 5 vs Fable 5
Claude Opus 5 vs Fable 5 coding agent benchmark: same repo-repair harness and Weave Evaluation scoring. Compare cost, partial-repair quality, and workflow.
Last updated: September 14, 2026
Coding models now work as agents that inspect repositories, diagnose defects, edit code, run tests, and decide when to stop. That makes a one-shot coding prompt a weak basis for comparison. A useful benchmark must measure the full repair process, including whether a patch generalizes beyond visible tests and whether the agent finishes without unsafe or incomplete actions.
We evaluated Claude Opus 5 and Fable 5 inside the same controlled repository environment. The benchmark covers three realistic repair projects with 140 source files, 9,578 source lines, 116 public tests, and 263 hidden tests. Each model receives the same issue, tools, execution limits, and grading logic.
We use W&B Weave Evaluation to run both models against the same dataset and apply the same scorer to every repair. Weave records each provider call, tool action, patch attempt, test run, score, token count, and cost, while keeping the final metrics linked to the underlying execution traces.
Under identical repositories, tools, budgets, and grading, does Fable complete more repairs than Opus, and is any reliability advantage worth the additional cost?
At a glance: Claude Opus 5 vs Fable 5
This Opus 5 vs Fable comparison is a multi-turn coding agent benchmark on repository repair. Both models ran under identical harness conditions with Weave Evaluation scoring.
| Dimension | Claude Opus 5 | Claude Fable 5 |
|---|---|---|
| Public / hidden tests (all tasks) | 112 / 116 · 252 / 263 | 112 / 116 · 252 / 263 |
| Full task resolution | None (partial repairs) | None (partial repairs) |
| Total cost (3 tasks, Weave) | $1.03 | $2.89 (~2.8×) |
| Workflow | Lower cost; one security flag | Cleaner finish; more agent tests |
Table of contents
Step 0: PrerequisitesStep 1: Configure Anthropic and WeaveClaude Opus 5 vs Fable 5What we are comparingStep 2: Repository-repair benchmarkThe three repository-repair tasksRunning the benchmark with Weave EvaluationRunning the benchmarkResultsWhat the results showConclusion

Image by author
Step 0: Prerequisites
Install the dependencies:
Use Python 3.11 or a compatible version, then install the required packages:
pip install -U anthropic weave wandb pytest
You need two API keys:
Add them to the project’s .env file:
ANTHROPIC_API_KEY=your_anthropic_key WANDB_API_KEY=your_wandb_key WEAVE_PROJECT=your_wandb_entity/Opus-5-vs-Fable ANTHROPIC_MODEL_OPUS=claude-opus-5 ANTHROPIC_MODEL_FABLE=claude-fable-5 MAX_MODEL_OUTPUT_TOKENS=7000 MAX_MODEL_TURNS=35

Image by author
Final cost is computed from provider-reported usage, including standard input and output tokens plus applicable prompt-cache creation and cache-read charges.
Prompt caching reduces repeated input costs by reusing stable content such as the system prompt, tool definitions, and earlier conversation context across agent turns.
Before starting a live benchmark, confirm that your Anthropic account has enough usage allowance for all planned attempts.
Step 1: Configure Anthropic and Weave
Initialize the Anthropic client and Weave, then verify that both model endpoints are working correctly.
import os import anthropic import weave WEAVE_PROJECT = "your_wandb_entity/Opus-5-vs-Fable" ANTHROPIC_MODEL_OPUS = "claude-opus-5" ANTHROPIC_MODEL_FABLE = "claude-fable-5" MAX_MODEL_OUTPUT_TOKENS = 7000 MAX_MODEL_TURNS = 35 client = anthropic.Anthropic( api_key=os.environ["ANTHROPIC_API_KEY"] ) weave.init(WEAVE_PROJECT) print("Environment configured") print("Output cap:", MAX_MODEL_OUTPUT_TOKENS) print("Turn limit:", MAX_MODEL_TURNS) @weave.op def claude_inference(prompt: str, model: str) -> str: response = client.messages.create( model=model, max_tokens=2000, messages=[ { "role": "user", "content": prompt, } ], ) return "".join( block.text for block in response.content if block.type == "text" ) prompt = """ Review this function and identify one correctness issue: def divide(a, b): return a / b """ opus_result = claude_inference( prompt=prompt, model=ANTHROPIC_MODEL_OPUS, ) fable_result = claude_inference( prompt=prompt, model=ANTHROPIC_MODEL_FABLE, )
The check confirms:
- Authentication
- Model configuration
- Shared Anthropic client behavior
- Weave tracing
- Usage capture
It is not the benchmark itself. The actual evaluation is multi-turn, tool-using, and repository-based.

Image by author
Claude Opus 5 vs Fable 5
Anthropic positions both models for complex coding and agentic work. Opus 5 emphasizes deep reasoning, code review, and long-horizon execution, while Fable 5 is positioned for sustained autonomous coding and knowledge-work tasks involving planning, tool use, and self-testing.
Product descriptions alone do not show how the models behave inside a constrained repair environment. This benchmark compares them using identical repositories, prompts, tools, limits, and grading rather than relying on model positioning or token price.
What we are comparing
The benchmark changes only the model. Opus 5 and Fable 5 receive the same frozen candidate repository, engineering issue, system prompt, nine tools, public test command, hidden grading, resource limits, output cap, wall time, and scorer.
Because the harness conditions are held constant, observed differences reflect the sampled model trajectories rather than different tools or grading conditions.
The benchmark does not assume that the model with the higher token price is automatically better. A more expensive model can still be cheaper per successful repair when it completes more tasks, uses fewer retries, or avoids human intervention. A lower-priced model may also achieve equivalent repair quality at lower total cost.
The central question is:
Which model produces valid, fully tested repository repairs more reliably, and what does each completed repair cost?
Step 2: Repository-repair benchmark
Each benchmark attempt starts from a fresh copy of a frozen candidate repository. The model receives a written engineering issue, the editable source tree, bounded test results, and protected-path rules.
The agent can use nine controlled tools:
- list_files
- read_file
- search_code
- apply_patch
- write_file
- run_public_tests
- run_agent_tests
- git_diff
- finish
The model can run the public test suite and inspect the returned failures, but it cannot read protected test files or any hidden grading material. It also has no unrestricted shell, arbitrary Python execution, reference implementation, expected-failure manifest, grading metadata, or access outside the isolated workspace.
Edits pass validation before they reach the workspace. The harness supports structured replacements, unified diffs, and bounded file writes, while checking allowed paths, source context, patch size, and workspace containment. Incomplete or truncated edits are discarded rather than executed.
Each full run allows up to 35 model turns, 75 tool calls, 40 file reads, 20 searches, 12 patch attempts, four public-test runs, and four agent-test runs. Provider responses are capped at 7,000 output tokens, and the run has a 30-minute wall-time limit.
The three repository-repair tasks
The benchmark uses three frozen projects that test different kinds of repository repair.

Image by author
The File-to-SQLite task asks the agent to repair durability and recovery failures in a migration workflow while preserving the existing public API and storage behavior.

Image by author
The Legacy API task asks the agent to preserve v1 and v2 behavior across retries, jobs, webhooks, authorization, pagination, and SDK usage.

Image by author
The transactional workflow task asks the agent to repair a durable order-processing system spanning inventory, payment, shipping, recovery, and concurrency.

Image by author
Together, the three tasks cover migration reliability, compatibility repair, and long-running transactional behavior. They provide a focused comparison rather than a universal model ranking.
Running the benchmark with Weave Evaluation
We used weave.Evaluation to run both models against the same frozen task dataset and score every repair with the same correctness, usage, and cost metrics.
Each repository is added to a weave.Dataset and passed to the multi-turn repair agent. The scorer then evaluates correctness, patch validity, protected-file integrity, security, tool usage, token consumption, and cost.
The scorer reports both repair quality and execution behavior. A run counts as fully resolved (fully_resolved) only when every public and hidden test passes, the patch is valid, protected files remain unchanged, and no security violations occur. The public test pass rate (public_pass_rate) measures performance on tests visible to the agent, while the hidden test pass rate (hidden_pass_rate) measures performance on withheld tests. Patch validity (patch_valid) confirms that the repaired repository remains compilable, importable, gradeable, and confined to allowed paths. Security violations (security_violations) count blocked attempts to perform disallowed operations.
The following implementation shows the full Weave Evaluation flow:
import json from pathlib import Path from typing import Any import weave TASKS = ( "legacy-api-modernization", "file-to-sqlite-migration", "transactional-order-workflow", ) TEST_RESULT_FIELDS = ( "passed", "failed", "errors", "skipped", ) PATCH_VALIDATION_FIELDS = ( "valid", "candidate_compiles", "candidate_imports", "workspace_gradeable", "changed_files_allowed", "workspace_contained", ) def require_fields( value: dict[str, Any], fields: tuple[str, ...], name: str, ): """Raise an error when required fields are missing.""" missing = [field for field in fields if field not in value] if missing: raise ValueError( f"{name} is missing required fields: " f"{', '.join(missing)}" ) def build_dataset( root: str | Path, selected_task: str | None = None, ): root = Path(root) tasks = (selected_task,) if selected_task else TASKS rows = [] for task in tasks: manifest_path = root / task / "task_manifest.json" manifest = json.loads( manifest_path.read_text(encoding="utf-8") ) require_fields( manifest, ("difficulty", "category", "dataset_version"), f"Manifest for {task}", ) rows.append({ "task": task, "difficulty": manifest["difficulty"], "category": manifest["category"], "manifest_path": manifest_path.as_posix(), "dataset_version": manifest["dataset_version"], }) return weave.Dataset( name="coding-agent-repair-benchmark", rows=rows, ) @weave.op() def comprehensive_repair_scorer(output, **_): require_fields( output, ( "public_tests", "hidden_tests", "patch_validation", "protected_hashes_before", "protected_hashes_after", ), "Agent output", ) public = output["public_tests"] hidden = output["hidden_tests"] patch = output["patch_validation"] require_fields( public, TEST_RESULT_FIELDS, "Public test results", ) require_fields( hidden, TEST_RESULT_FIELDS, "Hidden test results", ) require_fields( patch, PATCH_VALIDATION_FIELDS, "Patch validation", ) public_total = sum( public[field] for field in TEST_RESULT_FIELDS ) hidden_total = sum( hidden[field] for field in TEST_RESULT_FIELDS ) patch_valid = all( patch[field] for field in PATCH_VALIDATION_FIELDS ) protected_unchanged = ( output["protected_hashes_before"] == output["protected_hashes_after"] ) security_violation_records = output.get( "security_violation_records", [], ) tool_calls = output.get("tool_calls", []) model_calls = output.get("model_calls", []) costs = [ call["weave_total_cost"] for call in model_calls if call.get("weave_total_cost") is not None ] return { "fully_resolved": all([ public["failed"] == 0, public["errors"] == 0, hidden["failed"] == 0, hidden["errors"] == 0, patch_valid, protected_unchanged, len(security_violation_records) == 0, ]), "public_pass_rate": ( public["passed"] / public_total if public_total else 0 ), "hidden_pass_rate": ( hidden["passed"] / hidden_total if hidden_total else 0 ), "patch_valid": patch_valid, "protected_files_unchanged": protected_unchanged, "security_violations": len( security_violation_records ), "tool_calls": len(tool_calls), "total_tokens": sum( call.get("input_tokens", 0) + call.get("output_tokens", 0) for call in model_calls ), "estimated_cost": ( sum(costs) if costs else None ), } @weave.op() async def run_live_evaluation( root: str | Path, model, trials: int = 1, selected_task: str | None = None, ): evaluation = weave.Evaluation( evaluation_name=f"live-{model.model_name}", dataset=build_dataset(root, selected_task), scorers=[comprehensive_repair_scorer], trials=trials, ) return await evaluation.evaluate(model)
Running the same evaluation for Opus 5 and Fable 5 produces directly comparable results. Weave connects each score to its underlying trace, while Weave evaluation compares highlights task-level and overall differences side by side.
Running the benchmark
The evaluation code above defines the dataset and scorer. The next step is to create one repair agent for each configured model and run the same weave. Evaluation against the selected repository task.
from dataclasses import dataclass from datetime import datetime, timezone from pathlib import Path import weave @dataclass class BenchmarkConfig: weave_project: str = "your_wandb_entity/Opus-5-vs-Fable" opus_model: str = "claude-opus-5" fable_model: str = "claude-fable-5" seed: int = 42 cache_dir: Path = Path("benchmark-cache") run_dir: Path = Path("evaluation-runs") max_turns: int = 35 max_tool_calls: int = 75 max_file_reads: int = 40 max_searches: int = 20 max_patch_attempts: int = 12 max_patch_size: int = 200_000 max_public_test_runs: int = 4 max_agent_test_runs: int = 4 max_wall_seconds: int = 1800 max_model_output_tokens: int = 7000 context_compaction_turns: int = 12 provider_timeout: int = 300 subprocess_timeout: int = 300 provider_retries: int = 2 dataset_version: str = "1.0.0" harness_version: str = "2.1.0" def create_repair_agent(config, model_name): settings = { "model": model_name, "seed": config.seed, "harness_version": config.harness_version, "dataset_version": config.dataset_version, "max_turns": config.max_turns, "max_tool_calls": config.max_tool_calls, "max_file_reads": config.max_file_reads, "max_searches": config.max_searches, "max_patch_attempts": config.max_patch_attempts, "max_patch_size": config.max_patch_size, "max_public_test_runs": config.max_public_test_runs, "max_agent_test_runs": config.max_agent_test_runs, "max_wall_seconds": config.max_wall_seconds, "max_model_output_tokens": config.max_model_output_tokens, "context_compaction_turns": config.context_compaction_turns, "provider_timeout": config.provider_timeout, "subprocess_timeout": config.subprocess_timeout, "provider_retries": config.provider_retries, } return RepairAgent( model_name=model_name, seed=config.seed, agent_config_hash=digest(settings), max_turns=config.max_turns, max_tool_calls=config.max_tool_calls, max_file_reads=config.max_file_reads, max_searches=config.max_searches, max_patch_attempts=config.max_patch_attempts, max_patch_size=config.max_patch_size, max_public_test_runs=config.max_public_test_runs, max_agent_test_runs=config.max_agent_test_runs, max_wall_seconds=config.max_wall_seconds, max_output_tokens=config.max_model_output_tokens, context_compaction_turns= config.context_compaction_turns, provider_timeout=config.provider_timeout, subprocess_timeout=config.subprocess_timeout, provider_retries=config.provider_retries, run_dir=config.run_dir.as_posix(), cache_dir=config.cache_dir.as_posix(), ) def initialize_weave(project, model_names): if not project: raise ValueError("WEAVE_PROJECT is missing") client = weave.init(project) pricing = { "claude-fable-5": ( 10.0, 50.0, datetime(2026, 6, 9, tzinfo=timezone.utc), ), "claude-opus-5": ( 5.0, 25.0, datetime(2026, 7, 24, tzinfo=timezone.utc), ), } selected = { name for name in model_names if name in pricing } existing = { cost.llm_id for cost in client.query_costs( llm_ids=sorted(selected) ) } for model_name in sorted(selected - existing): input_price, output_price, effective_date = pricing[model_name] client.add_cost( llm_id=model_name, prompt_token_cost=input_price / 1_000_000, completion_token_cost=output_price / 1_000_000, effective_date=effective_date, ) return client ROOT = Path(".").resolve() config = BenchmarkConfig() SELECTED_TASK = "legacy-api-modernization" RUNS_PER_MODEL = 1 MODELS = [config.opus_model, config.fable_model] initialize_weave( project=config.weave_project, model_names=MODELS, ) async def evaluate_models(): results = {} for model_name in MODELS: agent = create_repair_agent(config, model_name) results[model_name] = await run_live_evaluation( ROOT, agent, RUNS_PER_MODEL, SELECTED_TASK, ) return results evaluation_results = await evaluate_models() evaluation_results
This cell makes paid provider calls for both models. Each run uses the same selected task, trial count, tools, limits, and scorer. The returned summaries are logged to Weave Evaluation, where each score remains linked to the underlying repair trace.
To benchmark another repository, change `SELECTED_TASK` and rerun the cell.

Image by author
Results
Across all three repository tasks, Opus 5 and Fable 5 reached the same final repair quality. Both models passed 112 of 116 public tests and 252 of 263 hidden tests. Neither fully resolved a task, but both produced valid partial repairs while preserving protected files.

Image by author
Opus achieved the same aggregate test results at a Weave-reported cost of $1.0334, compared with $2.8902 for Fable. Fable cost about 2.80 times as much, or Opus about 64.2% less. Fable used fewer provider calls, tool calls, and file reads, while Opus produced fewer output tokens.
Legacy API modernization
Both models started with 38 of 44 public tests and 73 of 84 hidden tests passing. They finished with 40 public and 80 hidden tests passing, adding two public and seven hidden passes.
Neither model completed the remaining persistence-related fixes, and both stopped after reaching the rejected-edit limit. Repair quality was tied, but the Weave Evaluation recorded a cost of $0.2136 for Opus and $0.4976 for Fable. Opus therefore achieved the same result for about 57% less cost.

Image by author
File-to-SQLite migration
Both models repaired all four public failures, moving from 30 of 34 to 34 of 34 public tests. Hidden performance improved from 69 of 75 to 72 of 75.
Fable used fewer provider calls, tool calls, file reads, and rejected edits. It also passed all three agent-created tests, called finish, and recorded no security violations. Opus did not invoke the finish tool and recorded one security violation, but remained cheaper at $0.3783 compared with $0.6930 for Fable.

Image by author
Transactional order workflow
Both models preserved the complete public baseline of 38 tests and improved hidden performance from 95 of 104 to 100 of 104.
Fable added four passing regression tests and explicitly invoked the finish tool. Opus created no agent tests and stopped without invoking the finish tool. Despite those workflow differences, both models received the same official score.
The cost difference was the largest on this task. The Weave Evaluation recorded a cost of $0.4415 for Opus and $1.6996 for Fable. Fable cost about 3.85 times as much. Four hidden failures remained for both models, so neither achieved full resolution.
Overall, the benchmark found no difference in official repair quality across the sampled runs. Opus was consistently cheaper, while Fable showed stronger workflow discipline and cleaner termination behavior.
What the results show
The sampled runs reveal a clear trade-off between cost efficiency and agent workflow discipline. Across all three repository tasks, Opus 5 and Fable 5 reached exactly the same official test totals: 112 of 116 public tests and 252 of 263 hidden tests. Neither model fully resolved a repository, so the benchmark does not identify a winner on complete repair reliability. Instead, it shows two models reaching equivalent partial-repair quality through different execution patterns.
Opus 5 was the stronger option in terms of cost. Its three official runs totaled $1.0334 in Weave, compared with $2.8902 for Fable 5. Fable therefore cost about 2.80 times as much, or Opus about 64.2% less. Opus also produced fewer output tokens, 41,303 versus 57,773. The largest task-level difference appeared on the transactional order workflow, where Opus cost $0.4415, and Fable cost $1.6996.
Fable 5 showed cleaner execution behavior. It used fewer provider calls, tool calls, and file reads overall. It also created more passing agent tests, called finish more consistently, and completed the benchmark without a recorded security violation. Opus recorded one blocked violation during the file-to-SQLite task, although protected files remained unchanged.
The task-level pattern was consistent. Both models made the same improvements on every repository, but Opus did so more cheaply, while Fable followed a more deliberate workflow.

Image by author
Conclusion
This benchmark did not produce a clear quality winner. Opus 5 and Fable 5 ended with identical public and hidden test totals, while leaving some failures unresolved.
That is itself a finding worth trusting, not just a null result. Without execution-level visibility, "no difference" is easy to get wrong: it can hide a model that quietly failed more tests and recovered, or a scorer that missed a security violation. Because Weave traced every provider call, tool action, and test run behind each score, we can say with confidence that the tie is real, not an artifact of loose measurement.
The more useful distinction was how they worked. Opus delivered the same measured outcome at a much lower cost, making it the stronger option for efficiency. Fable followed a more controlled workflow, with more consistent testing, cleaner termination, and no recorded security violations.
That difference matters in practice. A coding agent should be judged not only by the final patch, but also by how safely, predictably, and economically it reaches that result. W&B Weave made that judgment possible here: it's what let us confirm the outcome was tied while still surfacing the operational differences underneath it.
Add a comment
Iterate on AI agents and models faster. Try Weights & Biases today.