# Phase 15 — Retarget chat, count_tokens, and list_models specs

**Estimated time:** ~30 minutes
**Touches:**
`spec/dispatch/adapter/minimax/chat_spec.rb`,
`spec/dispatch/adapter/minimax/chat_non_streaming_spec.rb`,
`spec/dispatch/adapter/minimax/chat_streaming_spec.rb`,
`spec/dispatch/adapter/minimax/chat_streaming_retry_spec.rb`,
`spec/dispatch/adapter/minimax/count_tokens_spec.rb`,
`spec/dispatch/adapter/minimax/list_models_spec.rb`.

## Goal

Update the integration-style specs that drive the public adapter API
through HTTP stubs. The major changes:

- `stub_request(...).to_return(...)` URLs change from
  `https://api.anthropic.com/v1/messages` to
  `https://api.minimax.io/anthropic/v1/messages` (and likewise for
  `/v1/messages/count_tokens` and `/v1/models`).
- Adapter constructor no longer takes `token_path:`, `is_oauth:`,
  `token_store:`, or `user_agent_override:`. It now takes `api_key:`,
  optionally `key_path:`, and the other surviving kwargs from phase
  04.
- Authentication header check changes from
  `X-Api-Key` / OAuth-bearer logic to a simple
  `Authorization: Bearer <key>`.
- No more `anthropic-version`, `anthropic-beta`, `x-stainless-*`, or
  `User-Agent` header presence assertions — those headers are gone.
- Response and SSE bodies come from the new fixtures.
- Cost assertions expect `0.0`.

## Pre-reading

Read each spec file before editing:

1. `spec/dispatch/adapter/minimax/chat_spec.rb` (top-level adapter
   chat orchestration)
2. `spec/dispatch/adapter/minimax/chat_non_streaming_spec.rb`
3. `spec/dispatch/adapter/minimax/chat_streaming_spec.rb`
4. `spec/dispatch/adapter/minimax/chat_streaming_retry_spec.rb`
5. `spec/dispatch/adapter/minimax/count_tokens_spec.rb`
6. `spec/dispatch/adapter/minimax/list_models_spec.rb`

Also re-skim:

- `lib/dispatch/adapter/minimax.rb` (the public class — what kwargs
  does the constructor take post-phase-04?)
- `spec/fixtures/sse/*.sse` and `spec/fixtures/responses/*.json`
  (post-phase-12 content)

## Common changes across all six specs

### 1. Adapter construction

OLD (typical):

```ruby
adapter = described_class.new(
  api_key: "test-key",
  token_path: tmp_path,
  is_oauth: false,
  base_url: "https://api.anthropic.com"
)
```

NEW:

```ruby
adapter = described_class.new(
  api_key: "test-key",
  key_path: tmp_path,            # only if the test exercises file-loading
  base_url: "https://api.minimax.io/anthropic"
)
```

Drop `is_oauth:`, `token_path:`, `token_store:`, `user_agent_override:`,
`extra_betas:`, `interleaved_thinking:` from every call.

### 2. URL stubs

```ruby
stub_request(:post, "https://api.minimax.io/anthropic/v1/messages")
stub_request(:post, "https://api.minimax.io/anthropic/v1/messages/count_tokens")
stub_request(:get,  "https://api.minimax.io/anthropic/v1/models")
```

### 3. Header-presence assertions

Remove every `with(headers: ...)` assertion that mentioned
`anthropic-version`, `anthropic-beta`, `x-stainless-*`, `User-Agent`
(claude-cli), or `X-Api-Key`.

Keep the assertion that the request includes:

```ruby
"Authorization" => "Bearer test-key",
"Content-Type"  => "application/json"
```

For streaming requests:

```ruby
"Accept" => "text/event-stream"
```

For non-streaming:

```ruby
"Accept" => "application/json"
```

### 4. Response model id assertions

Replace any `expect(response.model).to eq("claude-sonnet-...")` with
`expect(response.model).to eq("MiniMax-M2.7")`.

### 5. Token / cost assertions

Token counts now match the new fixtures (15/8 for text, 50/18 for
tool-use, 42/24 for thinking). Cost assertions: any
`expect(usage.cost.total_cost).to be > 0` becomes
`expect(usage.cost.total_cost).to eq(0.0)`.

### 6. Drop cloaking / OAuth / billing-payload examples

DELETE any example that asserted on:

- The synthetic billing-payload system block.
- Auto-generated `metadata.user_id`.
- `proxy_<toolname>` request shape on the wire.
- Bearer-vs-X-Api-Key branching from the `sk-ant-oat` prefix.
- The `claude-3-5-haiku` skip-billing-block special case.
- `usage_report` (separate spec was already deleted in phase 02; if
  any reference survived, delete it).

### 7. Strict-fallback retry examples (chat_streaming_retry_spec)

Update the 400-error fixture used to trigger the fallback to use one
of MiniMax's plausible error shapes. The spec should still verify:

- On a 400 with grammar/schema-too-large/complex error, the request is
  retried once with `disable_strict_tools: true` forwarded to the
  request builder.
- `@strict_disabled = true` latch is set after the first fallback.
- Subsequent calls automatically pass `disable_strict_tools: true`
  without re-incurring the fallback round-trip.

### 8. count_tokens spec

If MiniMax's `/v1/messages/count_tokens` endpoint returns an HTTP 404
(unknown — we are guessing), the existing graceful degradation
(`rescue StandardError; -1`) should kick in. Add a test case for that:

```ruby
it "returns -1 when count_tokens endpoint is unavailable" do
  stub_request(:post, "https://api.minimax.io/anthropic/v1/messages/count_tokens")
    .to_return(status: 404, body: '{"error":{"message":"not found"}}')

  expect(adapter.count_tokens(messages: [...], system: nil, tools: [])).to eq(-1)
end
```

But also keep a successful-path test that stubs a 200 with
`{"input_tokens": 15}` so the happy path is exercised.

### 9. list_models spec

Same pattern: add tests for both the happy path (stubbed `/v1/models`
returning a `data: [...]` array) and the fallback (404 → returns the
hardcoded 7-model catalog from `PricingTable.known_ids`).

The spec should verify that when both runtime and bundled lists are
available, the result is deduplicated by id (no model appears twice).

## Acceptance criteria

- All six retargeted specs pass cleanly.
- No `.skip` or `pending` examples remain in any.
- `grep -n 'api.anthropic.com\|claude-sonnet\|claude-3\|sk-ant-oat\|is_oauth\|proxy_\|claude-cli\|x-stainless\|anthropic-beta\|anthropic-version\|X-Api-Key' spec/dispatch/adapter/minimax/chat*.rb spec/dispatch/adapter/minimax/{count_tokens,list_models}_spec.rb`
  returns ZERO matches.
- `bundle exec rubocop --autocorrect-all` exits 0.

## Verification

Run `run_tests` with `project_path=reference/dispatch-adapter-minimax`.
Rubocop must be clean. The six retargeted specs must pass. Failures in
the smaller specs (model_catalog, pricing, errors, strict_fallback,
http_client, rate_limiter, main `minimax_spec.rb`) are acceptable here
and addressed in phase 16.
