From 2d48f4c306e552954693ce894200822caeef23aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thireus=20=E2=98=A0?= Date: Mon, 21 Sep 2026 19:28:49 +0100 Subject: [PATCH] ui: give the renderMessages probes the block helper Four upstream tests read renderMessages out of ui.js and run a slice of it against a fake segment object. Since the message block goes in through _insertSegmentBlock, three of them stopped at "ReferenceError: _insertSegmentBlock is not defined" and the fourth still pinned the old seg.insertAdjacentHTML line, which took twelve of the fifteen test shards red on this branch and nowhere else. test_status_command_card.py defines the helper in its own harness, one line that does what the real helper does without a browser. test_compaction_current_summary_fallback.py and test_anchor_fallback_ownership.py evaluate the real helper from ui.js alongside the functions they already evaluate; their DOM shims return no template content, so it takes its insertAdjacentHTML fallback exactly as before. test_sprint49.py pins the new call, and its point is unchanged: the footer still rides in the same block as the body. A new case in tests/test_issue2051_duplicate_assistant_answer.py pins why the helper exists, which answers a review note: the block is parsed into a template and the nodes are MOVED, so a repeated call adds nothing, and insertAdjacentHTML survives only as the fallback. All three insertions go through it. tests/test_anchor_fallback_ownership.py, tests/test_compaction_current_summary_fallback.py, tests/test_sprint49.py, tests/test_status_command_card.py: 34 passed. The three #2051 files: 12 passed. --- tests/test_anchor_fallback_ownership.py | 5 +++ ...est_compaction_current_summary_fallback.py | 4 +++ ...st_issue2051_duplicate_assistant_answer.py | 32 +++++++++++++++++++ tests/test_sprint49.py | 5 ++- tests/test_status_command_card.py | 7 ++++ 5 files changed, 52 insertions(+), 1 deletion(-) diff --git a/tests/test_anchor_fallback_ownership.py b/tests/test_anchor_fallback_ownership.py index 50c041e7..6e61dd2b 100644 --- a/tests/test_anchor_fallback_ownership.py +++ b/tests/test_anchor_fallback_ownership.py @@ -416,6 +416,10 @@ def test_render_messages_keeps_anchor_owned_turn_out_of_legacy_activity_rebuilds legacy_metadata_source = _function_source( _ui_js(), "_legacySettledFallbackHasToolMetadata" ) + # #2051: renderMessages() inserts a message block through this helper, so it is + # evaluated with it. The shim's createElement() returns no template `content`, so the + # helper takes its insertAdjacentHTML fallback here, exactly as before. + insert_block_source = _function_source(_ui_js(), "_insertSegmentBlock") script = textwrap.dedent( f""" class FakeClassList {{ @@ -702,6 +706,7 @@ def test_render_messages_keeps_anchor_owned_turn_out_of_legacy_activity_rebuilds eval({json.dumps(transparent_source)}); eval({json.dumps(legacy_metadata_source)}); + eval({json.dumps(insert_block_source)}); eval({json.dumps(render_source)}); const toolResult = {{ role: 'tool', tool_call_id: 'toolu_1', content: 'tool result' }}; diff --git a/tests/test_compaction_current_summary_fallback.py b/tests/test_compaction_current_summary_fallback.py index d0ec6aaf..276ac311 100644 --- a/tests/test_compaction_current_summary_fallback.py +++ b/tests/test_compaction_current_summary_fallback.py @@ -50,6 +50,10 @@ _REAL_FUNCTIONS = ( "_insertPreservedCompressionTaskFallback", "_pinCompactionCardAtTop", "_pinSettledCompressionReferenceAtTop", + # #2051: renderMessages() inserts a message block through this helper, so it has to be + # evaluated with it. The shim's createElement() returns no template `content`, so the + # helper takes its insertAdjacentHTML fallback here, exactly as before. + "_insertSegmentBlock", "renderMessages", ) diff --git a/tests/test_issue2051_duplicate_assistant_answer.py b/tests/test_issue2051_duplicate_assistant_answer.py index 6493bf16..1eb63ac0 100644 --- a/tests/test_issue2051_duplicate_assistant_answer.py +++ b/tests/test_issue2051_duplicate_assistant_answer.py @@ -336,3 +336,35 @@ def test_both_append_branches_are_gated_by_the_ownership_check(): "the segment-swap and whole-turn replace branches must stay unguarded — " "they replace a node instead of adding one and cannot duplicate" ) + + +def test_segment_block_insertion_moves_nodes_instead_of_reparsing_html(): + """Wiring: every message-block insertion in renderMessages() goes through the + helper, and the helper MOVES parsed nodes instead of re-parsing a string. + + Appending a DocumentFragment moves its children and empties it, so a DOM-API + wrapper that executes the call twice appends nothing the second time; an + insertAdjacentHTML wrapper that does the same parses and appends a second copy. + That difference is the whole fix, so it is pinned here rather than left to the + three call sites. + """ + helper = UI_JS[UI_JS.index("function _insertSegmentBlock("):] + helper = helper[:helper.index("\nfunction renderMessages")] + assert "createElement('template')" in helper, ( + "the block must be parsed into a