# Phase 06 — Pricing zero

**Estimated time:** ~15 minutes
**Touches:** `lib/dispatch/adapter/minimax/pricing_table.rb`,
`lib/dispatch/adapter/minimax/data/claude_pricing.json` (or whatever the
JSON data file is named after phase 01),
`lib/dispatch/adapter/minimax/response_builder.rb` (cost calculation).

## Goal

Token Plan is request-quota, not per-token. Setting per-token rates would
mislead callers. Instead, keep the `Usage#cost` field for API stability
but always populate it as `0.0`.

`PricingTable` becomes a stub: it answers `context_window` /
`max_output_tokens` / `lookup` correctly for known MiniMax models, but
`lookup` returns a `ModelPricing` whose four `*_per_mtok` fields are all
zero.

## Steps

### 1. Replace the pricing JSON data file

The current data file (likely
`lib/dispatch/adapter/minimax/data/claude_pricing.json`) contains
Anthropic per-MTok rates. Replace it with a MiniMax catalog containing
zeros for every cost field. RENAME the file to
`lib/dispatch/adapter/minimax/data/minimax_pricing.json`.

```json
{
  "MiniMax-M2.7": {
    "context_window": 204800,
    "max_output_tokens": 16384,
    "input_per_mtok": 0.0,
    "output_per_mtok": 0.0,
    "cache_read_per_mtok": 0.0,
    "cache_write_per_mtok": 0.0
  },
  "MiniMax-M2.7-highspeed": {
    "context_window": 204800,
    "max_output_tokens": 16384,
    "input_per_mtok": 0.0,
    "output_per_mtok": 0.0,
    "cache_read_per_mtok": 0.0,
    "cache_write_per_mtok": 0.0
  },
  "MiniMax-M2.5": {
    "context_window": 204800,
    "max_output_tokens": 16384,
    "input_per_mtok": 0.0,
    "output_per_mtok": 0.0,
    "cache_read_per_mtok": 0.0,
    "cache_write_per_mtok": 0.0
  },
  "MiniMax-M2.5-highspeed": {
    "context_window": 204800,
    "max_output_tokens": 16384,
    "input_per_mtok": 0.0,
    "output_per_mtok": 0.0,
    "cache_read_per_mtok": 0.0,
    "cache_write_per_mtok": 0.0
  },
  "MiniMax-M2.1": {
    "context_window": 204800,
    "max_output_tokens": 16384,
    "input_per_mtok": 0.0,
    "output_per_mtok": 0.0,
    "cache_read_per_mtok": 0.0,
    "cache_write_per_mtok": 0.0
  },
  "MiniMax-M2.1-highspeed": {
    "context_window": 204800,
    "max_output_tokens": 16384,
    "input_per_mtok": 0.0,
    "output_per_mtok": 0.0,
    "cache_read_per_mtok": 0.0,
    "cache_write_per_mtok": 0.0
  },
  "MiniMax-M2": {
    "context_window": 204800,
    "max_output_tokens": 16384,
    "input_per_mtok": 0.0,
    "output_per_mtok": 0.0,
    "cache_read_per_mtok": 0.0,
    "cache_write_per_mtok": 0.0
  }
}
```

`max_output_tokens` is set to 16,384 because MiniMax's documented
`max_tokens` field caps generation; the figure is a conservative default
(the Anthropic SDK request shape uses `max_tokens` and MiniMax accepts up
to whatever the model supports; we'll let MiniMax bound it server-side
for anything larger).

### 2. Update `lib/dispatch/adapter/minimax/pricing_table.rb`

- Change `DATA_PATH` to point at the new filename:
  `File.expand_path("data/minimax_pricing.json", __dir__)`.
- Leave the four module-level helper methods (`lookup`, `context_window`,
  `max_output_tokens`, `known_ids`) UNCHANGED. They will return correct
  values from the new zeroed JSON.

### 3. Confirm `Usage#cost` plumbing

In `lib/dispatch/adapter/minimax/response_builder.rb`, find the
`build_usage` method (and / or `build_usage_cost` if separate). The
existing logic computes cost as `(input_tokens * input_per_mtok / 1M) +
…`. With every `*_per_mtok` field zero, this naturally evaluates to 0.0
— no logic change is needed.

DO NOT short-circuit cost to a hardcoded `0.0`. Let it flow through the
existing arithmetic so that if a future MiniMax pricing model is
populated, the same plumbing will Just Work.

If the existing code calls `pricing.input_per_mtok * input_tokens` and
`pricing` can be `nil` for unknown models, ensure it gracefully returns
`0.0` (not `nil`) when `pricing` is nil. Add a guard if missing:

```ruby
return UsageCost.new(input_cost: 0.0, output_cost: 0.0,
                     cache_read_cost: 0.0, cache_write_cost: 0.0,
                     total_cost: 0.0) if pricing.nil?
```

(Field names should match whatever `Dispatch::Adapter::UsageCost`
actually exposes — check the interface gem if uncertain.)

### 4. Delete or update specs that asserted on real prices

If `spec/dispatch/adapter/minimax/pricing_table_spec.rb` has examples
that expect non-zero values for Anthropic models, those examples will
fail after this phase. Update them to reflect MiniMax's zero rates and
the seven new model IDs. The full retarget is in phase 16, but minor
adjustments here to keep the spec passing are fine — DO NOT weaken
or skip examples; either rewrite them correctly now or expect their
failures to be fixed in phase 16.

If you choose to defer to phase 16, ensure the failures are clearly
attributable to "expected Anthropic prices, got zero" (not a load error,
not a syntax error, not a NoMethodError).

## Acceptance criteria

- The JSON data file is named `minimax_pricing.json` and lives under
  `lib/dispatch/adapter/minimax/data/`.
- `claude_pricing.json` does NOT exist.
- `PricingTable.known_ids` returns exactly the 7 MiniMax model IDs.
- `PricingTable.context_window("MiniMax-M2.7")` returns `204_800`.
- `PricingTable.lookup("MiniMax-M2.7").input_per_mtok` is `0.0`.
- `PricingTable.lookup("nonexistent-model")` returns `nil`.
- `bundle exec rubocop --autocorrect-all` exits 0.

## Verification

Run `run_tests` with `project_path=reference/dispatch-adapter-minimax`.
Rubocop must be clean. The `pricing_table_spec.rb` failures are
acceptable as long as they only stem from old Anthropic price assertions
(retargeted in phase 16). Any failure NOT of that shape must be fixed
before calling `ask_for_next_plan`.
