fix(renderers): bridge merge must not mutate prior multi_modal_data#104
Merged
Conversation
The multimodal bridge_to_next_turn merge built its carried-forward sidecars
with dict(previous_multi_modal_data.mm_*), which copies only the outer dict —
the per-modality lists stayed shared with the caller. Appending this turn's
items via .setdefault(modality, []).extend() then mutated those original
lists in place, so a reused prior RenderedTokens / MultiModalData grew extra
placeholders/items/hashes after bridging.
Copy the per-modality lists ({k: list(v) ...}) in all three affected
renderers — KimiK25Renderer (flagged by Cursor Bugbot on the Inkling PR),
plus Qwen3VLRenderer and Qwen35Renderer, which carried the identical pattern.
Extends the shared test_multimodal_bridge_extends_and_carries_mm_data with a
(2b) assertion that the prior turn's sidecar is unchanged after bridging.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ApprovabilityVerdict: Approved Straightforward bug fix that replaces shallow dict copies with deep copies of inner lists to prevent mutation of caller data. The change is consistent across 3 renderer files, includes test coverage, and the author is fixing their own code. You can customize Macroscope's approvability policy. Learn more. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
MultimodalRenderer.bridge_to_next_turnmerged the carried-forward multimodal sidecar withdict(previous_multi_modal_data.mm_*)— which copies only the outer dict. The per-modality lists stayed shared with the caller, so appending this turn's items via.setdefault(modality, []).extend(...)mutated the caller'sprevious_multi_modal_datain place. A reused priorRenderedTokens/MultiModalDatawould silently grow extra placeholders / items / hashes after a bridge.Fix: copy the per-modality lists (
{k: list(v) for k, v in ….items()}) before extending.Scope
Cursor Bugbot flagged this on the Inkling PR (#103) for
KimiK25Renderer; the identical pattern was inQwen3VLRendererandQwen35Renderer, so all three are fixed here. (Inkling itself was fixed in #103.)Test
Extends the shared
test_multimodal_bridge_extends_and_carries_mm_datawith a (2b) assertion that the prior turn's sidecar counts are unchanged after bridging — it now runs green for all five multimodal checkpoints (Qwen3-VL, Qwen3.5, Qwen3.6, Kimi K2.5, Kimi K2.6). Fulltests/test_multimodal.py: 85 passed, 3 skipped.Comment-and-copy only; no token-stream or
bridgedoutput change.🤖 Generated with Claude Code
Note
Low Risk
Targeted copy-before-extend fix with no intended change to token streams; regression covered by extended multimodal bridge test.
Overview
Fixes in-place mutation of the caller's
previous_multi_modal_datawhen multimodal renderers merge carried-forward vision sidecars duringbridge_to_next_turn.Previously, merging used
dict(previous_multi_modal_data.mm_*), which only shallow-copied the outer map—per-modality lists stayed aliased, so.setdefault(modality, []).extend(...)could append placeholders, items, and hashes to a reused priorRenderedTokens/MultiModalData. The change deep-copies each modality list ({k: list(v) for k, v in ...}) in Kimi K2.5, Qwen3.5, and Qwen3-VL before extending; bridged token output is unchanged.Adds test (2b) in
test_multimodal_bridge_extends_and_carries_mm_dataasserting prior sidecar counts stay(1, 1, 1)after bridging.Reviewed by Cursor Bugbot for commit a5f0278. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Fix
bridge_to_next_turnmutation of caller-suppliedprevious_multi_modal_dataThe
bridge_to_next_turnmethod inKimiK25Renderer,Qwen35Renderer, andQwen3VLRendererwas shallow-copying only the outer dict when merging prior multimodal sidecar data, causing.extend()calls to mutate the caller's original per-modality lists. Each renderer now uses{k: list(v) for k, v in ....items()}to copy the inner lists, isolating the merged copy from the caller's data. A regression test in test_multimodal.py now asserts that per-modality counts inprevious_multi_modal_dataremain unchanged after bridging.Macroscope summarized a5f0278.