summaryrefslogtreecommitdiffhomepage
path: root/.rules/plan/12-25-update-fixtures.md
diff options
context:
space:
mode:
Diffstat (limited to '.rules/plan/12-25-update-fixtures.md')
-rw-r--r--.rules/plan/12-25-update-fixtures.md263
1 files changed, 263 insertions, 0 deletions
diff --git a/.rules/plan/12-25-update-fixtures.md b/.rules/plan/12-25-update-fixtures.md
new file mode 100644
index 0000000..d39d24b
--- /dev/null
+++ b/.rules/plan/12-25-update-fixtures.md
@@ -0,0 +1,263 @@
+# Phase 12 — Update fixtures
+
+**Estimated time:** ~25 minutes
+**Touches:** every file under `spec/fixtures/sse/` and
+`spec/fixtures/responses/`.
+
+## Goal
+
+Replace the Anthropic-shaped fixtures with MiniMax-shaped fixtures that
+the rest of the spec suite (phases 13–16) will rely on.
+
+The MiniMax `/anthropic` endpoint mirrors Anthropic's wire format, so
+the fixture STRUCTURE is unchanged — same SSE event names, same JSON
+keys, same content blocks. The data INSIDE the fixtures changes:
+
+- `model` field becomes `MiniMax-M2.7` (or similar MiniMax id).
+- Token counts and message ids can be plausible placeholders.
+- Tool-use blocks keep the Anthropic shape (`{"type":"tool_use","id":"...","name":"...","input":{...}}`).
+- The previously-deleted OAuth fixtures (`oauth-profile.json`,
+ `oauth-usage-full.json`, `oauth-usage-partial.json`) are GONE per
+ phase 02; do not recreate them.
+
+## Steps
+
+### 1. Inventory the existing fixtures
+
+The fixtures that must remain (renamed/rewritten):
+
+```text
+spec/fixtures/sse/text-only.sse
+spec/fixtures/sse/thinking-then-text.sse
+spec/fixtures/sse/tool-use.sse
+spec/fixtures/sse/truncated-before-message-start.sse
+spec/fixtures/sse/truncated-mid-text.sse
+spec/fixtures/responses/messages-text.json
+spec/fixtures/responses/messages-tool-use.json
+spec/fixtures/responses/messages-with-thinking.json
+```
+
+Already deleted in phase 02:
+
+```text
+spec/fixtures/responses/oauth-profile.json
+spec/fixtures/responses/oauth-usage-full.json
+spec/fixtures/responses/oauth-usage-partial.json
+```
+
+### 2. Rewrite `spec/fixtures/sse/text-only.sse`
+
+```text
+event: message_start
+data: {"type":"message_start","message":{"id":"msg_minimax_text01","type":"message","role":"assistant","content":[],"model":"MiniMax-M2.7","stop_reason":null,"usage":{"input_tokens":15,"output_tokens":0}}}
+
+event: content_block_start
+data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
+
+event: ping
+data: {}
+
+event: content_block_delta
+data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello, "}}
+
+event: content_block_delta
+data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"world!"}}
+
+event: content_block_stop
+data: {"type":"content_block_stop","index":0}
+
+event: message_delta
+data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":8}}
+
+event: message_stop
+data: {"type":"message_stop"}
+
+```
+
+(Keep the trailing blank line.)
+
+### 3. Rewrite `spec/fixtures/sse/thinking-then-text.sse`
+
+```text
+event: message_start
+data: {"type":"message_start","message":{"id":"msg_minimax_thnk01","type":"message","role":"assistant","content":[],"model":"MiniMax-M2.7","stop_reason":null,"usage":{"input_tokens":42,"output_tokens":0}}}
+
+event: content_block_start
+data: {"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":"","signature":""}}
+
+event: content_block_delta
+data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"Considering the question..."}}
+
+event: content_block_delta
+data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":" the answer is two."}}
+
+event: content_block_stop
+data: {"type":"content_block_stop","index":0}
+
+event: content_block_start
+data: {"type":"content_block_start","index":1,"content_block":{"type":"text","text":""}}
+
+event: content_block_delta
+data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"The answer is 2."}}
+
+event: content_block_stop
+data: {"type":"content_block_stop","index":1}
+
+event: message_delta
+data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":24}}
+
+event: message_stop
+data: {"type":"message_stop"}
+
+```
+
+NOTE: MiniMax docs do not document `signature` on thinking blocks. The
+parser must accept thinking blocks WITHOUT a signature too. Leaving
+`"signature":""` here exercises the empty-string branch; if the
+existing parser treats empty-string as "no signature", that's fine.
+
+### 4. Rewrite `spec/fixtures/sse/tool-use.sse`
+
+```text
+event: message_start
+data: {"type":"message_start","message":{"id":"msg_minimax_tool01","type":"message","role":"assistant","content":[],"model":"MiniMax-M2.7","stop_reason":null,"usage":{"input_tokens":50,"output_tokens":0}}}
+
+event: content_block_start
+data: {"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"toolu_minimax01","name":"get_weather","input":{}}}
+
+event: content_block_delta
+data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"{\"city\":"}}
+
+event: content_block_delta
+data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"\"Boston\"}"}}
+
+event: content_block_stop
+data: {"type":"content_block_stop","index":0}
+
+event: message_delta
+data: {"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"output_tokens":18}}
+
+event: message_stop
+data: {"type":"message_stop"}
+
+```
+
+### 5. Rewrite `spec/fixtures/sse/truncated-before-message-start.sse`
+
+```text
+event: ping
+data: {}
+
+```
+
+(Two lines, ending in a blank line. The point of this fixture is that
+the stream ends before any `message_start` arrives — the streaming
+retry harness should treat this as a transient first-event failure.)
+
+### 6. Rewrite `spec/fixtures/sse/truncated-mid-text.sse`
+
+```text
+event: message_start
+data: {"type":"message_start","message":{"id":"msg_minimax_trunc01","type":"message","role":"assistant","content":[],"model":"MiniMax-M2.7","stop_reason":null,"usage":{"input_tokens":15,"output_tokens":0}}}
+
+event: content_block_start
+data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
+
+event: content_block_delta
+data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"
+```
+
+(Deliberately ends mid-frame, no terminating blank line. The streaming
+retry harness should NOT retry once consumer-visible text has been
+emitted.)
+
+### 7. Rewrite `spec/fixtures/responses/messages-text.json`
+
+```json
+{
+ "id": "msg_minimax_resp_text01",
+ "type": "message",
+ "role": "assistant",
+ "model": "MiniMax-M2.7",
+ "content": [
+ { "type": "text", "text": "Hello, world!" }
+ ],
+ "stop_reason": "end_turn",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 15,
+ "output_tokens": 8
+ }
+}
+```
+
+### 8. Rewrite `spec/fixtures/responses/messages-tool-use.json`
+
+```json
+{
+ "id": "msg_minimax_resp_tool01",
+ "type": "message",
+ "role": "assistant",
+ "model": "MiniMax-M2.7",
+ "content": [
+ {
+ "type": "tool_use",
+ "id": "toolu_minimax_resp01",
+ "name": "get_weather",
+ "input": { "city": "Boston" }
+ }
+ ],
+ "stop_reason": "tool_use",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 50,
+ "output_tokens": 18
+ }
+}
+```
+
+### 9. Rewrite `spec/fixtures/responses/messages-with-thinking.json`
+
+```json
+{
+ "id": "msg_minimax_resp_thnk01",
+ "type": "message",
+ "role": "assistant",
+ "model": "MiniMax-M2.7",
+ "content": [
+ {
+ "type": "thinking",
+ "thinking": "Considering the question... the answer is two.",
+ "signature": ""
+ },
+ { "type": "text", "text": "The answer is 2." }
+ ],
+ "stop_reason": "end_turn",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 42,
+ "output_tokens": 24
+ }
+}
+```
+
+## Acceptance criteria
+
+- All five SSE fixtures exist with the new MiniMax-flavoured content.
+- All three response JSON fixtures exist with the new MiniMax-flavoured
+ content.
+- `grep -rn 'claude-sonnet\|claude-3-5\|claude-3.5\|anthropic' spec/fixtures/`
+ returns ZERO matches.
+- `bundle exec rubocop --autocorrect-all` exits 0 (no Ruby files
+ changed, but invoked via `run_tests`).
+
+## Verification
+
+Run `run_tests` with `project_path=reference/dispatch-adapter-minimax`.
+Rubocop must be clean. RSpec failures pointing at the fixtures
+(expected text strings, model ids, token counts) are routine and will
+be reconciled when the corresponding spec is retargeted in phase
+13–16. Any failure NOT of that shape must be investigated.
+
+DO NOT update spec assertions to match the fixtures here — that is
+deferred to the retarget phases.