Context
rskim-llm (#302) ships Anthropic body mutation via list_blocks / mutate_block (byte-surgery text-for-text replacement). OpenAI /v1/chat/completions bodies are parsed and classified, but mutation is not yet implemented:
list_blocks(ParsedBody::OpenAi(_)) returns an empty Vec.
mutate_block on an OpenAI body returns LlmError::BlockNotMutable(id, "openai-not-implemented").
classify_body does walk OpenAI content and emits ids of the form m{i} / m{i}p{j}, but those ids are not addressable through list_blocks / mutate_block (documented asymmetry in lib.rs).
Scope
Implement OpenAI text-payload mutation symmetric with the Anthropic path:
- Byte-surgery splice for OpenAI string content (
messages[i].content string) and content-part text (messages[i].content[j].text where type == "text").
- Keep the no-envelope-mutation invariant absolute (Resolved Decision 7):
model, tool_calls, tool_call_id, reasoning, function.arguments remain sacrosanct / exempt (Resolved Decision 6).
list_blocks returns mutable descriptors for OpenAI bodies; ids align with classify_body.
- Round-trip byte-identity + repeat-mutation idempotency tests mirroring the Anthropic suite.
Note
Tracked because the source previously referenced a wrong issue number (#329 is a rskim-tokens follow-up). This is the correct tracker (ADR-004: follow-ups filed and correctly linked, no orphaned refs / AC18).
Context
rskim-llm(#302) ships Anthropic body mutation vialist_blocks/mutate_block(byte-surgery text-for-text replacement). OpenAI/v1/chat/completionsbodies are parsed and classified, but mutation is not yet implemented:list_blocks(ParsedBody::OpenAi(_))returns an emptyVec.mutate_blockon an OpenAI body returnsLlmError::BlockNotMutable(id, "openai-not-implemented").classify_bodydoes walk OpenAI content and emits ids of the formm{i}/m{i}p{j}, but those ids are not addressable throughlist_blocks/mutate_block(documented asymmetry inlib.rs).Scope
Implement OpenAI text-payload mutation symmetric with the Anthropic path:
messages[i].contentstring) and content-part text (messages[i].content[j].textwheretype == "text").model,tool_calls,tool_call_id,reasoning,function.argumentsremain sacrosanct / exempt (Resolved Decision 6).list_blocksreturns mutable descriptors for OpenAI bodies; ids align withclassify_body.Note
Tracked because the source previously referenced a wrong issue number (#329 is a rskim-tokens follow-up). This is the correct tracker (ADR-004: follow-ups filed and correctly linked, no orphaned refs / AC18).