summaryrefslogtreecommitdiffhomepage
path: root/.rules/plan/11-15-errors-module.md
diff options
context:
space:
mode:
Diffstat (limited to '.rules/plan/11-15-errors-module.md')
-rw-r--r--.rules/plan/11-15-errors-module.md123
1 files changed, 123 insertions, 0 deletions
diff --git a/.rules/plan/11-15-errors-module.md b/.rules/plan/11-15-errors-module.md
new file mode 100644
index 0000000..7a11798
--- /dev/null
+++ b/.rules/plan/11-15-errors-module.md
@@ -0,0 +1,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`.