summaryrefslogtreecommitdiffhomepage
path: root/.rules/plan/11-15-errors-module.md
blob: 7a11798d762702299b26e12eff92d52b9204f520 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# Phase 11 — Errors module rename and retarget

**Estimated time:** ~15 minutes
**Touches:** `lib/dispatch/adapter/minimax/errors.rb` and every file
that references `ClaudeErrors` or `OverloadedError`.

## Goal

The errors module is currently `Dispatch::Adapter::ClaudeErrors`.
Rename it to `Dispatch::Adapter::MiniMaxErrors`. The mapping logic
(401/403 → AuthenticationError, 429 → RateLimitError, 529 →
OverloadedError, 400/422 → RequestError, 5xx → ServerError) stays the
same. Update the `PROVIDER` constant.

The class `OverloadedError < RateLimitError` stays — Anthropic uses 529
to signal overload and MiniMax MAY too; even if MiniMax never sends
529, the class is harmless dead branch coverage.

## Steps

### 1. Rewrite `lib/dispatch/adapter/minimax/errors.rb`

```ruby
# frozen_string_literal: true

module Dispatch
  module Adapter
    class OverloadedError < RateLimitError; end

    module MiniMaxErrors
      module_function

      PROVIDER = "MiniMax"

      def handle_response!(response)
        return if response.is_a?(Net::HTTPSuccess)

        code = response.code.to_i
        msg  = parse_message(response.body)
        retry_after = response["Retry-After"]&.to_i

        case code
        when 401, 403 then raise AuthenticationError.new(msg, status_code: code, provider: PROVIDER)
        when 429      then raise RateLimitError.new(msg, status_code: code, provider: PROVIDER, retry_after:)
        when 529      then raise OverloadedError.new(msg, status_code: code, provider: PROVIDER, retry_after:)
        when 400, 422 then raise RequestError.new(msg, status_code: code, provider: PROVIDER)
        when 500..599 then raise ServerError.new(msg, status_code: code, provider: PROVIDER)
        else raise Error.new(msg, status_code: code, provider: PROVIDER)
        end
      end

      def parse_message(body)
        parsed = JSON.parse(body.to_s)
        # Anthropic shape: {"error":{"message":"..."}}
        # MiniMax may also use the same shape; keep the dig.
        return parsed.dig("error", "message").to_s unless parsed.dig("error", "message").nil?
        # Fallback: top-level "message" key (some MiniMax surfaces use this).
        return parsed["message"].to_s if parsed["message"]
        body.to_s
      rescue JSON::ParserError
        body.to_s
      end
    end
  end
end
```

Notes on `parse_message`:

- Adds a fallback for a top-level `"message"` key. We don't yet know
  MiniMax's exact error JSON shape, but supporting both `{error:{message}}`
  and `{message}` is cheap and forwards-compatible.
- If neither matches, falls back to the raw body.

### 2. Find every reference to `ClaudeErrors` and update

```bash
grep -rn 'ClaudeErrors\|ClaudeErrors::PROVIDER' lib/ spec/ sig/
```

Replace each occurrence with `MiniMaxErrors`. Common locations:
- `lib/dispatch/adapter/minimax/sse_parser.rb`
  (`raise RequestError.new(..., provider: ClaudeErrors::PROVIDER)`)
- `lib/dispatch/adapter/minimax/http_client.rb`
- `lib/dispatch/adapter/minimax/stream_collector.rb`
- `lib/dispatch/adapter/minimax/usage_client.rb` — DELETED in phase 02,
  shouldn't appear, but double-check.
- `lib/dispatch/adapter/minimax.rb` (top-level adapter)

Also confirm the rbs sig file mirrors the rename:

```bash
grep -n 'ClaudeErrors' sig/dispatch/adapter/minimax.rbs
```

If there's a sig declaration for `ClaudeErrors`, rename to
`MiniMaxErrors`.

### 3. Confirm `errors.rb` is required

`lib/dispatch/adapter/minimax.rb` should have
`require_relative "minimax/errors"` near the top of its requires list.
If phase 01 left it as `require_relative "minimax/errors"` already, no
change needed. The new module name `MiniMaxErrors` is autoloaded via
that file.

## Acceptance criteria

- `grep -rn 'ClaudeErrors' lib/ spec/ sig/` returns ZERO matches.
- `Dispatch::Adapter::MiniMaxErrors::PROVIDER == "MiniMax"`.
- `Dispatch::Adapter::MiniMaxErrors.handle_response!(...)` raises the
  correct mapped error class for each status code as documented above.
- `parse_message('{"error":{"message":"oops"}}') == "oops"`.
- `parse_message('{"message":"oops"}') == "oops"`.
- `parse_message("not json") == "not json"`.
- `bundle exec rubocop --autocorrect-all` exits 0.

## Verification

Run `run_tests` with `project_path=reference/dispatch-adapter-minimax`.
Rubocop must be clean. `errors_spec.rb` failures referencing
`ClaudeErrors` are addressed in phase 16. Failures NOT explainable by
that rename must be fixed before calling `ask_for_next_plan`.