Skip to content

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 implement and archive phases): 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 ​

json
{
  "executor": {
    "provider": "opencode",
    "opencode": {
      "model": "opencode-go/deepseek-v4-pro"
    }
  }
}

Or per run:

bash
OPENSPEC_SHIPPER_OPENCODE_MODEL=opencode-go/deepseek-v4-pro npx openspec-shipper queue run

OpenCode 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 ​

json
{
  "executor": {
    "provider": "codex-cli",
    "codex": {
      "model": "gpt-5.6-luna",
      "reasoningEffort": "xhigh"
    }
  }
}

Or per run:

bash
OPENSPEC_SHIPPER_CODEX_MODEL=gpt-5.6-luna \
OPENSPEC_SHIPPER_CODEX_REASONING_EFFORT=xhigh \
npx openspec-shipper queue run

reasoningEffort 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 ​

json
{
  "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:

bash
OPENSPEC_SHIPPER_CLAUDE_MODEL=sonnet \
OPENSPEC_SHIPPER_CLAUDE_EFFORT=low \
npx openspec-shipper queue run

sonnet with low effort is a solid implementation default; reserve bigger models for changes you already know are gnarly.

Measure before you decide ​

bash
npx openspec-shipper queue stats

Stats 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.

Released under the MIT License.