# Phase 13 — Retarget headers and request-builder specs

**Estimated time:** ~25 minutes
**Touches:**
`spec/dispatch/adapter/minimax/headers_spec.rb`,
`spec/dispatch/adapter/minimax/request_builder_spec.rb`.

## Goal

Update the two specs that exercise the most heavily-modified code:
the simplified `Headers.build` (phase 04) and the MiniMax-specific
constraints in `RequestBuilder.build` (phases 08, 09, 10).

## Pre-reading

Before editing, read these files in full so you know the current
shapes:

1. `lib/dispatch/adapter/minimax/headers.rb`
2. `lib/dispatch/adapter/minimax/request_builder.rb`
3. `spec/dispatch/adapter/minimax/headers_spec.rb`
4. `spec/dispatch/adapter/minimax/request_builder_spec.rb`

## Steps for headers_spec.rb

### 1. Delete every example that asserted on removed behavior

DELETE examples covering:

- `claude_code_only` mode
- `interleaved_thinking` flag handling
- `extra_betas` merging
- `anthropic-version` header presence
- `x-stainless-*` header presence
- `User-Agent: claude-cli/...` header
- OAuth token detection (`sk-ant-oat` prefix)
- The `is_oauth:` keyword
- `anthropic-beta` header construction

Removing these examples is NOT "weakening tests" — they target
deleted code. The behavior they covered no longer exists.

### 2. Replace with a focused MiniMax-only suite

The new spec should cover ONLY the new contract:

```ruby
# frozen_string_literal: true

require "spec_helper"

RSpec.describe Dispatch::Adapter::MiniMax::Headers do
  describe ".build" do
    it "always sets Authorization to Bearer <api_key>" do
      headers = described_class.build(api_key: "k_abc")
      expect(headers["Authorization"]).to eq("Bearer k_abc")
    end

    it "always sets Content-Type to application/json" do
      headers = described_class.build(api_key: "k_abc")
      expect(headers["Content-Type"]).to eq("application/json")
    end

    it "sets Accept to application/json when stream is false" do
      headers = described_class.build(api_key: "k_abc", stream: false)
      expect(headers["Accept"]).to eq("application/json")
    end

    it "sets Accept to text/event-stream when stream is true" do
      headers = described_class.build(api_key: "k_abc", stream: true)
      expect(headers["Accept"]).to eq("text/event-stream")
    end

    it "lets caller extras pass through" do
      headers = described_class.build(api_key: "k", extra: { "X-Trace-Id" => "abc" })
      expect(headers["X-Trace-Id"]).to eq("abc")
    end

    it "never lets caller extras override Authorization" do
      headers = described_class.build(api_key: "k", extra: { "Authorization" => "Bearer evil" })
      expect(headers["Authorization"]).to eq("Bearer k")
    end

    it "does NOT include anthropic-version, x-stainless, or User-Agent headers" do
      headers = described_class.build(api_key: "k")
      expect(headers).not_to have_key("anthropic-version")
      expect(headers).not_to have_key("anthropic-beta")
      expect(headers.keys.grep(/x-stainless/i)).to eq([])
      expect(headers).not_to have_key("User-Agent")
    end
  end
end
```

## Steps for request_builder_spec.rb

### 1. Delete every example covering removed concerns

DELETE examples covering:

- `is_oauth:` parameter behavior (keyword removed in phase 02).
- `proxy_` prefixing of tool names (cloaking deleted in phase 02).
- `metadata: { user_id: ... }` cloaking-derived defaults
  (still emit a metadata block when caller supplies one, but no auto-
  generated user_id).
- Billing-payload synthetic system block.
- The `claude-3-5-haiku` skip-billing-block special case.

### 2. Update remaining examples to MiniMax shape

For each surviving example:

- Replace `"claude-sonnet-4-5-20250929"` (or whatever the old default
  was) with `"MiniMax-M2.7"` in `model` fields and assertions.
- Remove `is_oauth:` from any `RequestBuilder.build` call.
- Update `system` assertion expectations: with cloaking gone, the
  system block is exactly what the caller passed (string → wrapped in
  one text block; array → passed through; nil → omitted).

### 3. Add new examples for MiniMax constraints

Append new examples that exercise the work from phases 08, 09, 10:

```ruby
context "temperature validation" do
  it "raises ArgumentError when temperature is 0.0" do
    expect {
      described_class.build(model_id: "MiniMax-M2.7", messages: [], system: nil,
                            tools: [], base_url: "https://api.minimax.io/anthropic",
                            temperature: 0.0)
    }.to raise_error(ArgumentError, /temperature/)
  end

  it "raises ArgumentError when temperature exceeds 1.0" do
    expect {
      described_class.build(model_id: "MiniMax-M2.7", messages: [], system: nil,
                            tools: [], base_url: "https://api.minimax.io/anthropic",
                            temperature: 1.01)
    }.to raise_error(ArgumentError, /temperature/)
  end

  it "raises ArgumentError when temperature is negative" do
    expect {
      described_class.build(model_id: "MiniMax-M2.7", messages: [], system: nil,
                            tools: [], base_url: "https://api.minimax.io/anthropic",
                            temperature: -0.1)
    }.to raise_error(ArgumentError, /temperature/)
  end

  it "accepts temperature 1.0" do
    body = described_class.build(model_id: "MiniMax-M2.7", messages: [],
                                 system: nil, tools: [],
                                 base_url: "https://api.minimax.io/anthropic",
                                 temperature: 1.0)
    expect(body[:temperature]).to eq(1.0)
  end

  it "omits temperature when nil" do
    body = described_class.build(model_id: "MiniMax-M2.7", messages: [],
                                 system: nil, tools: [],
                                 base_url: "https://api.minimax.io/anthropic",
                                 temperature: nil)
    expect(body).not_to have_key(:temperature)
  end
end

context "ignored parameters" do
  it "strips top_k from the wire body even if forced via extras" do
    body = described_class.build(
      model_id: "MiniMax-M2.7", messages: [], system: nil, tools: [],
      base_url: "https://api.minimax.io/anthropic"
    )
    body[:top_k] = 5  # simulate accidental injection
    described_class.send(:strip_ignored!, body)
    expect(body).not_to have_key(:top_k)
  end

  # Mirror examples for stop_sequences, service_tier, mcp_servers,
  # context_management, container.
end

context "image / document content rejection" do
  it "raises ArgumentError when an ImageBlock is present" do
    skip "interface gem must define ImageBlock" unless defined?(Dispatch::Adapter::ImageBlock)

    msg = Dispatch::Adapter::Message.new(
      role: "user",
      content: [Dispatch::Adapter::ImageBlock.new(source: { type: "base64", media_type: "image/png", data: "" })]
    )
    expect {
      described_class.build(
        model_id: "MiniMax-M2.7", messages: [msg], system: nil, tools: [],
        base_url: "https://api.minimax.io/anthropic"
      )
    }.to raise_error(ArgumentError, /MiniMax does not support image/)
  end
end
```

NOTE on `skip`: the rule says "no skipped or pending examples after
phase 17". If the interface gem actually exposes `ImageBlock` /
`DocumentBlock`, REMOVE the `skip` and replace with concrete
construction. Verify by reading
`reference/dispatch-adapter-minimax/Gemfile.lock` →
`dispatch-adapter-interface` source location, then grep that gem for
`class ImageBlock` / `class DocumentBlock`. If they exist, write the
spec without `skip`. If they don't exist, REMOVE the example entirely
(do not leave a `skip`). Document briefly in a comment why no example
exists for image rejection.

## Acceptance criteria

- `headers_spec.rb` and `request_builder_spec.rb` both pass cleanly
  (no `.skip`, no `.pending`).
- `grep -n 'is_oauth\|sk-ant-oat\|claude_code_only\|anthropic-beta\|x-stainless\|claude-cli\|proxy_\|interleaved_thinking\|extra_betas' spec/dispatch/adapter/minimax/headers_spec.rb spec/dispatch/adapter/minimax/request_builder_spec.rb`
  returns ZERO matches.
- `bundle exec rubocop --autocorrect-all` exits 0.

## Verification

Run `run_tests` with `project_path=reference/dispatch-adapter-minimax`.
Both rubocop and the two retargeted spec files must pass cleanly. Other
spec failures are acceptable here (handled in phases 14–16).
