Pick the Right Model for Each Job
Goal of this page: spend expensive model capacity where it pays off, and cheap capacity where it doesn't matter.
The principle: plan expensive, implement cheap
A good OpenSpec change does the hard thinking up front: the proposal explains why, the design settles how, and tasks.md breaks the work into small, verifiable steps. Once that exists, implementation is mostly following instructions — and following instructions is exactly what smaller, cheaper models are good at.
So split your model budget:
- Planning (writing proposals, designs, and tasks): use your most capable model, in whatever tool you plan with. This is where quality is decided.
- Implementation (Shipper's
implementandarchivephases): configure a cheaper, faster model. The spec keeps it on rails, the repository checks catch its mistakes, and your PR review is the final gate.
If a cheap model repeatedly fails a particular change, that's usually feedback about the spec — tasks too big, ambiguous acceptance criteria — more often than about the model.
Shipper also uses the configured provider and model for the single assisted recovery attempt it may make before blocking an actionable failure. There is no separate recovery model: choose an implementation model capable of inspecting logs, editing the isolated worktree, and running focused checks.
Configuring the model per provider
You can set the model at init time, in .openspec-shipper/config.json, or override it per run with environment variables. Precedence is: CLI flags > OPENSPEC_SHIPPER_* env vars > .openspec-shipper/.env > config.json > defaults.
OpenCode
{
"executor": {
"provider": "opencode",
"opencode": {
"model": "opencode-go/deepseek-v4-pro"
}
}
}Or per run:
OPENSPEC_SHIPPER_OPENCODE_MODEL=opencode-go/deepseek-v4-pro npx openspec-shipper queue runOpenCode is the easiest place to experiment with cheap implementation models, since it can route to many providers — try a budget model on a low-stakes change and compare queue stats runs.
Codex CLI
{
"executor": {
"provider": "codex-cli",
"codex": {
"model": "gpt-5.6-luna",
"reasoningEffort": "xhigh"
}
}
}Or per run:
OPENSPEC_SHIPPER_CODEX_MODEL=gpt-5.6-luna \
OPENSPEC_SHIPPER_CODEX_REASONING_EFFORT=xhigh \
npx openspec-shipper queue runreasoningEffort controls how deeply Codex reasons. The default is xhigh for maximum reliability on implementation tasks; use low, medium, or high when speed or cost matters more.
Claude Code
{
"executor": {
"provider": "claude-code",
"claude": {
"model": "sonnet",
"effort": "low"
}
}
}Also settable at init (npx openspec-shipper init --provider claude-code --model sonnet --effort low) or per run:
OPENSPEC_SHIPPER_CLAUDE_MODEL=sonnet \
OPENSPEC_SHIPPER_CLAUDE_EFFORT=low \
npx openspec-shipper queue runsonnet with low effort is a solid implementation default; reserve bigger models for changes you already know are gnarly.
Measure before you decide
npx openspec-shipper queue statsStats show what each run cost. Try the same kind of change on two models and let the numbers — and your PR reviews — pick the winner.
What's next
One last page on habits: Ship like a team of two.