Keep evidence artifacts out of the cherry-pick path

This commit is contained in:
Rod Boev committed 2026-06-23 02:56:15 -04:00
1 parent e032cbb222
commit 11a3abeca1
5 files changed
-294

No files matched your search

-111
View File
@@ -1,111 +0,0 @@
# Test Evidence Summary — DOM Node Recycling PR
## Test counts
| Suite | Count | Status |
|-------|-------|--------|
| Recycling + scrollbar drag (pytest) | 48 | 48/48 pass |
| Existing virtualization regression (pytest) | 29 | 29/29 pass |
| CDP behavioral (live browser) | 8 | 8/8 pass |
| Native scrollbar drag (SendInput + CDP) | 4 | 4/4 pass |
| **Total** | **89** | **89 pass, 0 skip** |
## Maintainer must-fix items (PR #4474 CHANGES_REQUESTED)
### MF-1: data-msg-idx on .assistant-turn corrupts measurement
**Finding:** `querySelector('[data-msg-idx="N"]')` returns the `.assistant-turn` container (height 560px) instead of the `.assistant-segment` child (height 120px), inflating virtual window padding by 440px per turn.
**Fix:** Use `data-recycle-key` on `.assistant-turn`, leaving `data-msg-idx` exclusively on `.assistant-segment`.
**Evidence (test_mf1_multi_segment_turn_raw_heights):**
- 3-segment turn: segments at 120px, 90px, 150px with tool cards at 60px, 80+60px
- Measured heights: seg5=180px (120+60), seg6=230px (90+80+60), seg7=150px
- Total measured: 560px (correct sum of segments + tools)
- Buggy path would return: 560px for the FIRST SEGMENT ALONE (4.67x inflation)
**Evidence (test_mf1_buggy_path_inflates_height):**
- Buggy (container match): 560px
- Fixed (segment match): 120px
- Inflation: 440px per turn, 4.67x factor
### MF-2: un-typed stash lookups allow cross-type recycling
**Finding:** Without type guards, index shift between renders could hand a user row to the assistant branch or vice versa, causing blank-chat via null dereference.
**Fix:** `classList.contains('msg-row')` guard on user branch, `classList.contains('assistant-turn')` guard on assistant branch.
**Evidence (test_mf2_user_row_in_assistant_slot_without_guard):**
- Without guard: accepts user row (3 mismatched fields: className, dataset.role, id)
- With guard: rejects → falls back to fresh build
**Evidence (test_mf2_assistant_turn_in_user_slot_without_guard):**
- Without guard: accepts assistant turn (3 mismatched fields: className, dataset.role, dataset.msgIdx)
- With guard: rejects → falls back to fresh build
**Evidence (test_mf2_stash_collision_rate_in_shifted_indices):**
- 5 nodes stashed, indices shift by +2 (prepend scenario)
- Without guards: cross-type collisions occur at shifted boundary
- With guards: all collisions rejected, fresh nodes built instead
### MF-3: source-text greps pass on broken code
**Finding:** Original tests were `assertIn(pattern, source_text)` which pass regardless of whether the code path works.
**Fix:** Replaced with behavioral tests that extract and execute functions against mock DOM, producing concrete pixel values and node counts.
**Evidence (test_mf3_measurement_test_fails_on_buggy_layout):**
- Buggy layout: returns 560px (test would FAIL the 120px assertion)
- Fixed layout: returns 120px (test PASSES)
- Test sensitivity confirmed: different outputs on buggy vs fixed
**Evidence (test_mf3_type_check_test_fails_on_unguarded_code):**
- Guarded: rejects wrong-type node (null)
- Unguarded: accepts wrong-type node (non-null)
- Test sensitivity confirmed: opposite results with/without guard
## Scrollbar drag handling
**Problem:** Virtual scroll re-renders during native scrollbar drag need to keep `scrollHeight` stable, otherwise the thumb jumps on release.
**Approach:** Detect scrollbar pointerdown via `offsetX >= clientWidth`. During drag, run full renders (with DOM recycling) using `_compensateScrollForMeasurementDelta` instead of the normal path. This keeps the rendered DOM in sync with the scroll position throughout the drag, so `scrollHeight` stays stable and the thumb doesn't jump when the user releases.
**Why not spacer-only updates:** An earlier iteration used spacer-only updates during drag (updating spacer heights without rebuilding DOM). This caused `scrollHeight` to drift by 10,000+ px because the stale rendered content didn't match the new spacer heights. On release, the full render would correct the content, but the `scrollHeight` change shifted the thumb position by ~40% of the viewport.
**Why full renders work:** The scroll container (`#messages`) is never destroyed; only its inner container (`#msgInner`) gets `innerHTML = ''`. The browser maintains the native pointer grab on the scrollbar because the scrollbar belongs to `#messages`, not `#msgInner`. DOM recycling further reduces the churn by reattaching surviving nodes after the wipe.
**Evidence (native scrollbar drag, Windows SendInput + CDP):**
- 12-step drag from near-top to near-bottom of scrollbar track
- Total scroll delta: 13,522px
- All 12 steps monotonically increasing, 0 reversals
- `_scrollbarDragActive` flag: `true` during drag, `false` after release
- 53 content nodes, 0 blanks after release
- `scrollHeight` delta on release: 49px (0.06% of total, vs 10,710px / 14.8% with spacer-only)
**Evidence (CDP behavioral, synthetic PointerEvents):**
- Scrollbar click (offsetX >= clientWidth): flag activates
- Content click (offsetX < clientWidth): flag does NOT activate
- pointerup: flag clears
- pointercancel: flag clears
- DOM children count preserved during drag (65 → 65)
## Research-informed design
Virtual scroll libraries surveyed: react-virtuoso, TanStack Virtual, react-window, react-virtualized.
**Key finding:** None implement DOM node recycling. This pattern is novel to this codebase.
**Adopted from research:**
- TanStack Virtual's spatial threshold (1.5px) vs temporal (150ms) for programmatic scroll detection — we use a 150ms safety valve
- Debounced clear pattern from react-virtuoso's scroll-to-index implementation — our `_deferClearProgrammaticScroll`
- No library tests scrollbar drag interaction; our SendInput-based test is novel
**Test architecture:**
- Tier 1: Structural (flag/stash existence, lifecycle) — 6 tests
- Tier 2: Bug-specific behavioral (measurement corruption, type guards, stuck flag) — 9 tests
- Tier 3: Integration (full stash→wipe→lookup cycles) — 8 tests
- Tier 4: Optimization correctness (content-skip, key coercion) — 3 tests
- Tier 5: Scrollbar drag (detection, full-render-during-drag, release) — 13 tests
- Tier 6: Maintainer regression tests (raw numerical evidence for each must-fix) — 9 tests
- CDP behavioral: live browser flag lifecycle and render suppression — 8 tests
- Native scrollbar: real OS mouse events via Windows SendInput + CDP — 4 tests
-30
View File
@@ -1,30 +0,0 @@
Connecting to ws://localhost:9222/devtools/page/3CC47AC2E288A6FE8670B6C1EAD90117
=== T1: Flag initial state ===
PASS: flag starts as false
=== T2: Pointerdown on scrollbar area ===
PASS: scrollbar click activates flag
=== T3: Pointerdown on content area ===
PASS: content click does not activate flag
=== T4: Pointerup clears flag ===
PASS: pointerup clears flag
=== T5: Pointercancel clears flag ===
PASS: pointercancel clears flag
=== T6: Spacer-only path ===
PASS: spacers present (before=315848px, after=13747px)
=== T7: DOM stability during drag (render suppression) ===
PASS: children preserved (65 -> 65)
=== T8: Full render after drag release ===
PASS: content rendered after drag release
==================================================
RESULTS: 8 passed, 0 failed, 8 total
==================================================
ALL CDP BEHAVIORAL TESTS PASSED
-42
View File
@@ -1,42 +0,0 @@
Connected to ws://localhost:9222/devtools/page/3CC47AC2E288A6FE8670B6C1EAD90117
Window bounds: {'left': -6, 'top': -6, 'width': 1720, 'height': 926, 'windowState': 'maximized'}
Messages element: {'left': 348, 'top': 38.88888931274414, 'right': 1706.2222900390625, 'bottom': 674.9375343322754, 'width': 1358.2222900390625, 'height': 636.0486450195312, 'clientWidth': 1348, 'scrollHeight': 358441, 'scrollTop': 317723.5625, 'scrollbarWidth': 10, 'dpr': 2.25, 'innerWidth': 1707, 'innerHeight': 791}
DPR=2.25, chrome header: 135px CSS
Viewport origin CSS: (7, 129)
Scrollbar screen X: 3837, track Y: 423-1764
Pre-drag scrollTop: 0
=== Native Scrollbar Drag via SendInput ===
Mouse down at (3837, 453)
_scrollbarDragActive after native pointerdown: True
Step 1/12: screen_y=562, scrollTop=556.4444580078125
Step 2/12: screen_y=671, scrollTop=1670.6666259765625
Step 3/12: screen_y=781, scrollTop=3339.5556640625
Step 4/12: screen_y=890, scrollTop=4521.77783203125
Step 5/12: screen_y=999, scrollTop=5158.22216796875
Step 6/12: screen_y=1108, scrollTop=6757.77783203125
Step 7/12: screen_y=1218, scrollTop=7540
Step 8/12: screen_y=1327, scrollTop=9349.3330078125
Step 9/12: screen_y=1436, scrollTop=10461.77734375
Step 10/12: screen_y=1545, scrollTop=11644.888671875
Step 11/12: screen_y=1655, scrollTop=12757.3330078125
Step 12/12: screen_y=1764, scrollTop=13522.6669921875
_scrollbarDragActive after release: False
=== RESULTS ===
Total scroll delta: 13522.6669921875px
Monotonic steps: 11/11
Reversals: 0
Flag set during drag: True
Flag cleared after release: True
Content nodes: 53, blanks: 0
==================================================
PASS: T1 Native drag moved scroll
PASS: T2 Monotonic scrolling
PASS: T3 No blank content after release
PASS: T4 Flag lifecycle (set during, cleared after)
ALL PASS: True
==================================================
-46
View File
@@ -1,46 +0,0 @@
============================= test session starts =============================
platform win32 -- Python 3.11.15, pytest-9.0.3, pluggy-1.6.0 -- C:\Apps\hermes\hermes-agent\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\Repos\hermes-webui-pr-4346-vscroll-footer-jitter
configfile: pytest.ini
plugins: anyio-4.12.1, langsmith-0.8.14, shard-0.1.2, timeout-2.4.0
collecting ... collected 29 items
Running 29 items in this shard: tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_virtualizes_older_history_but_keeps_recent_tail, tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_collapses_to_tail_only_near_bottom, tests/test_issue500_message_list_virtualization.py::test_render_messages_uses_virtual_window_and_spacer_measurement_path, tests/test_issue500_message_list_virtualization.py::test_measurement_uses_one_primary_row_and_adjacent_activity_siblings_only, tests/test_issue500_message_list_virtualization.py::test_virtual_keep_tail_count_stays_bounded_after_history_expands_render_window, tests/test_issue500_message_list_virtualization.py::test_virtual_prepended_height_delta_uses_prefix_cache_only_when_virtualized, tests/test_issue500_message_list_virtualization.py::test_virtual_question_jump_scroll_target_uses_visible_index_height_prefix, tests/test_issue500_message_list_virtualization.py::test_height_cache_preserves_measured_prefix_across_append_only_growth, tests/test_issue500_message_list_virtualization.py::test_height_cache_preserves_measured_suffix_across_prepended_history, tests/test_issue500_message_list_virtualization.py::test_measurement_refresh_budget_is_keyed_to_window_shape_not_pad_height, tests/test_issue500_message_list_virtualization.py::test_tool_rows_do_not_carry_message_measurement_hook, tests/test_issue500_message_list_virtualization.py::test_viewport_intersection_helper_detects_visible_rendered_rows_only, tests/test_issue500_message_list_virtualization.py::test_render_messages_has_one_shot_virtual_blank_viewport_fallback, tests/test_issue500_message_list_virtualization.py::test_virtual_blank_viewport_recovery_evicts_stale_cache_before_fallback, tests/test_issue500_message_list_virtualization.py::test_same_frame_restore_nudges_virtual_window_when_anchor_row_is_missing, tests/test_issue500_message_list_virtualization.py::test_virtualize_transcript_opt_out_forces_full_render_window, tests/test_issue500_message_list_virtualization.py::test_virtualize_transcript_gate_present_in_current_window_fn, tests/test_issue500_message_list_virtualization.py::test_message_virtual_default_height_for_role_returns_correct_heights, tests/test_issue500_message_list_virtualization.py::test_message_virtual_role_for_entry_classifies_tool_calls, tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_with_role_for_idx_uses_role_defaults, tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_cached_heights_override_role_defaults, tests/test_issue500_message_list_virtualization.py::test_offset_helpers_use_per_role_defaults_for_uncached_rows, tests/test_issue500_message_list_virtualization.py::test_compensate_scroll_for_measurement_delta_no_anchor_does_not_throw, tests/test_issue500_message_list_virtualization.py::test_compensate_scroll_for_measurement_delta_shifts_scroll_when_anchor_moves, tests/test_issue500_message_list_virtualization.py::test_compensate_scroll_for_measurement_delta_skips_small_delta, tests/test_issue500_message_list_virtualization.py::test_compensate_scroll_for_measurement_delta_sets_programmatic_scroll_flag, tests/test_issue500_message_list_virtualization.py::test_virtualized_render_uses_compensation_helper, tests/test_issue500_message_list_virtualization.py::test_scroll_listener_guards_programmatic_scroll_before_marking_active, tests/test_issue500_message_list_virtualization.py::test_clear_height_cache_resets_scroll_settle_globals
tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_virtualizes_older_history_but_keeps_recent_tail PASSED [ 3%]
tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_collapses_to_tail_only_near_bottom PASSED [ 6%]
tests/test_issue500_message_list_virtualization.py::test_render_messages_uses_virtual_window_and_spacer_measurement_path PASSED [ 10%]
tests/test_issue500_message_list_virtualization.py::test_measurement_uses_one_primary_row_and_adjacent_activity_siblings_only PASSED [ 13%]
tests/test_issue500_message_list_virtualization.py::test_virtual_keep_tail_count_stays_bounded_after_history_expands_render_window PASSED [ 17%]
tests/test_issue500_message_list_virtualization.py::test_virtual_prepended_height_delta_uses_prefix_cache_only_when_virtualized PASSED [ 20%]
tests/test_issue500_message_list_virtualization.py::test_virtual_question_jump_scroll_target_uses_visible_index_height_prefix PASSED [ 24%]
tests/test_issue500_message_list_virtualization.py::test_height_cache_preserves_measured_prefix_across_append_only_growth PASSED [ 27%]
tests/test_issue500_message_list_virtualization.py::test_height_cache_preserves_measured_suffix_across_prepended_history PASSED [ 31%]
tests/test_issue500_message_list_virtualization.py::test_measurement_refresh_budget_is_keyed_to_window_shape_not_pad_height PASSED [ 34%]
tests/test_issue500_message_list_virtualization.py::test_tool_rows_do_not_carry_message_measurement_hook PASSED [ 37%]
tests/test_issue500_message_list_virtualization.py::test_viewport_intersection_helper_detects_visible_rendered_rows_only PASSED [ 41%]
tests/test_issue500_message_list_virtualization.py::test_render_messages_has_one_shot_virtual_blank_viewport_fallback PASSED [ 44%]
tests/test_issue500_message_list_virtualization.py::test_virtual_blank_viewport_recovery_evicts_stale_cache_before_fallback PASSED [ 48%]
tests/test_issue500_message_list_virtualization.py::test_same_frame_restore_nudges_virtual_window_when_anchor_row_is_missing PASSED [ 51%]
tests/test_issue500_message_list_virtualization.py::test_virtualize_transcript_opt_out_forces_full_render_window PASSED [ 55%]
tests/test_issue500_message_list_virtualization.py::test_virtualize_transcript_gate_present_in_current_window_fn PASSED [ 58%]
tests/test_issue500_message_list_virtualization.py::test_message_virtual_default_height_for_role_returns_correct_heights PASSED [ 62%]
tests/test_issue500_message_list_virtualization.py::test_message_virtual_role_for_entry_classifies_tool_calls PASSED [ 65%]
tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_with_role_for_idx_uses_role_defaults PASSED [ 68%]
tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_cached_heights_override_role_defaults PASSED [ 72%]
tests/test_issue500_message_list_virtualization.py::test_offset_helpers_use_per_role_defaults_for_uncached_rows PASSED [ 75%]
tests/test_issue500_message_list_virtualization.py::test_compensate_scroll_for_measurement_delta_no_anchor_does_not_throw PASSED [ 79%]
tests/test_issue500_message_list_virtualization.py::test_compensate_scroll_for_measurement_delta_shifts_scroll_when_anchor_moves PASSED [ 82%]
tests/test_issue500_message_list_virtualization.py::test_compensate_scroll_for_measurement_delta_skips_small_delta PASSED [ 86%]
tests/test_issue500_message_list_virtualization.py::test_compensate_scroll_for_measurement_delta_sets_programmatic_scroll_flag PASSED [ 89%]
tests/test_issue500_message_list_virtualization.py::test_virtualized_render_uses_compensation_helper PASSED [ 93%]
tests/test_issue500_message_list_virtualization.py::test_scroll_listener_guards_programmatic_scroll_before_marking_active PASSED [ 96%]
tests/test_issue500_message_list_virtualization.py::test_clear_height_cache_resets_scroll_settle_globals PASSED [100%]
============================== warnings summary ===============================
tests/test_issue500_message_list_virtualization.py::test_message_virtual_window_virtualizes_older_history_but_keeps_recent_tail
C:\Apps\hermes\hermes-agent\venv\Lib\site-packages\discord\player.py:30: DeprecationWarning: 'audioop' is deprecated and slated for removal in Python 3.13
import audioop
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
======================== 29 passed, 1 warning in 5.74s ========================
-65
View File
@@ -1,65 +0,0 @@
============================= test session starts =============================
platform win32 -- Python 3.11.15, pytest-9.0.3, pluggy-1.6.0 -- C:\Apps\hermes\hermes-agent\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\Repos\hermes-webui-pr-4346-vscroll-footer-jitter
configfile: pytest.ini
plugins: anyio-4.12.1, langsmith-0.8.14, shard-0.1.2, timeout-2.4.0
collecting ... collected 48 items
Running 48 items in this shard: tests/test_issue4346_vscroll_footer_jitter.py::test_js_recycle_flag_exists, tests/test_issue4346_vscroll_footer_jitter.py::test_js_recycle_stash_exists, tests/test_issue4346_vscroll_footer_jitter.py::test_js_recycle_flag_lifecycle, tests/test_issue4346_vscroll_footer_jitter.py::test_js_stash_populated_before_wipe, tests/test_issue4346_vscroll_footer_jitter.py::test_assistant_turn_uses_recycle_key_not_msg_idx, tests/test_issue4346_vscroll_footer_jitter.py::test_no_stale_raf_settimeout_clear_pattern, tests/test_issue4346_vscroll_footer_jitter.py::TestBug1MeasurementSelectorCollision::test_measurement_returns_segment_height_not_turn_height, tests/test_issue4346_vscroll_footer_jitter.py::TestBug1MeasurementSelectorCollision::test_measurement_accumulates_tool_siblings, tests/test_issue4346_vscroll_footer_jitter.py::TestBug1MeasurementSelectorCollision::test_measurement_on_buggy_turn_container_returns_wrong_height, tests/test_issue4346_vscroll_footer_jitter.py::TestBug2TypeCheckRecycling::test_user_branch_rejects_assistant_turn_node, tests/test_issue4346_vscroll_footer_jitter.py::TestBug2TypeCheckRecycling::test_assistant_branch_rejects_user_row_node, tests/test_issue4346_vscroll_footer_jitter.py::TestBug3ProgrammaticScrollStuckFlag::test_defer_clear_debounces_and_clears_flag, tests/test_issue4346_vscroll_footer_jitter.py::TestBug3ProgrammaticScrollStuckFlag::test_defer_clear_second_call_cancels_first, tests/test_issue4346_vscroll_footer_jitter.py::TestBug3ProgrammaticScrollStuckFlag::test_scroll_handler_safety_valve_in_source, tests/test_issue4346_vscroll_footer_jitter.py::TestBug3ProgrammaticScrollStuckFlag::test_safety_valve_clears_stale_flag, tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_stash_populates_from_recycle_key_and_msg_idx, tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_stash_excludes_live_assistant_turn, tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_stash_excludes_nested_live_assistant_turn, tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_full_recycle_cycle_user_row, tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_full_recycle_cycle_assistant_turn, tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_cross_type_stash_collision_rejected, tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_recycling_disabled_when_flag_false, tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_recycle_key_does_not_pollute_measurement_selector, tests/test_issue4346_vscroll_footer_jitter.py::TestContentSkipOptimization::test_unchanged_content_skips_innerhtml_update, tests/test_issue4346_vscroll_footer_jitter.py::TestContentSkipOptimization::test_changed_content_updates_innerhtml, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_scrollbar_drag_flag_declared, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_pointerdown_sets_flag_when_on_scrollbar, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_pointerdown_guard_exists_in_source, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_pointerup_clears_flag_and_triggers_render, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_pointercancel_clears_flag, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragRenderDuringDrag::test_drag_guard_exists_in_render_scheduler, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragRenderDuringDrag::test_drag_path_uses_full_render, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragRenderDuringDrag::test_drag_path_sets_programmatic_scroll, tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragRenderDuringDrag::test_release_uses_same_render_path, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF1MeasurementCorruption::test_mf1_multi_segment_turn_raw_heights, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF1MeasurementCorruption::test_mf1_buggy_path_inflates_height, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF1MeasurementCorruption::test_mf1_queryselector_returns_segment_not_container, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF2CrossTypeCollision::test_mf2_user_row_in_assistant_slot_without_guard, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF2CrossTypeCollision::test_mf2_assistant_turn_in_user_slot_without_guard, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF2CrossTypeCollision::test_mf2_stash_collision_rate_in_shifted_indices, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF3TestSensitivity::test_mf3_measurement_test_fails_on_buggy_layout, tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF3TestSensitivity::test_mf3_type_check_test_failsLine truncated
tests/test_issue4346_vscroll_footer_jitter.py::test_js_recycle_flag_exists PASSED [ 2%]
tests/test_issue4346_vscroll_footer_jitter.py::test_js_recycle_stash_exists PASSED [ 4%]
tests/test_issue4346_vscroll_footer_jitter.py::test_js_recycle_flag_lifecycle PASSED [ 6%]
tests/test_issue4346_vscroll_footer_jitter.py::test_js_stash_populated_before_wipe PASSED [ 8%]
tests/test_issue4346_vscroll_footer_jitter.py::test_assistant_turn_uses_recycle_key_not_msg_idx PASSED [ 10%]
tests/test_issue4346_vscroll_footer_jitter.py::test_no_stale_raf_settimeout_clear_pattern PASSED [ 12%]
tests/test_issue4346_vscroll_footer_jitter.py::TestBug1MeasurementSelectorCollision::test_measurement_returns_segment_height_not_turn_height PASSED [ 14%]
tests/test_issue4346_vscroll_footer_jitter.py::TestBug1MeasurementSelectorCollision::test_measurement_accumulates_tool_siblings PASSED [ 16%]
tests/test_issue4346_vscroll_footer_jitter.py::TestBug1MeasurementSelectorCollision::test_measurement_on_buggy_turn_container_returns_wrong_height PASSED [ 18%]
tests/test_issue4346_vscroll_footer_jitter.py::TestBug2TypeCheckRecycling::test_user_branch_rejects_assistant_turn_node PASSED [ 20%]
tests/test_issue4346_vscroll_footer_jitter.py::TestBug2TypeCheckRecycling::test_assistant_branch_rejects_user_row_node PASSED [ 22%]
tests/test_issue4346_vscroll_footer_jitter.py::TestBug3ProgrammaticScrollStuckFlag::test_defer_clear_debounces_and_clears_flag PASSED [ 25%]
tests/test_issue4346_vscroll_footer_jitter.py::TestBug3ProgrammaticScrollStuckFlag::test_defer_clear_second_call_cancels_first PASSED [ 27%]
tests/test_issue4346_vscroll_footer_jitter.py::TestBug3ProgrammaticScrollStuckFlag::test_scroll_handler_safety_valve_in_source PASSED [ 29%]
tests/test_issue4346_vscroll_footer_jitter.py::TestBug3ProgrammaticScrollStuckFlag::test_safety_valve_clears_stale_flag PASSED [ 31%]
tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_stash_populates_from_recycle_key_and_msg_idx PASSED [ 33%]
tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_stash_excludes_live_assistant_turn PASSED [ 35%]
tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_stash_excludes_nested_live_assistant_turn PASSED [ 37%]
tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_full_recycle_cycle_user_row PASSED [ 39%]
tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_full_recycle_cycle_assistant_turn PASSED [ 41%]
tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_cross_type_stash_collision_rejected PASSED [ 43%]
tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_recycling_disabled_when_flag_false PASSED [ 45%]
tests/test_issue4346_vscroll_footer_jitter.py::TestRecycleStashIntegration::test_recycle_key_does_not_pollute_measurement_selector PASSED [ 47%]
tests/test_issue4346_vscroll_footer_jitter.py::TestContentSkipOptimization::test_unchanged_content_skips_innerhtml_update PASSED [ 50%]
tests/test_issue4346_vscroll_footer_jitter.py::TestContentSkipOptimization::test_changed_content_updates_innerhtml PASSED [ 52%]
tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_scrollbar_drag_flag_declared PASSED [ 54%]
tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_pointerdown_sets_flag_when_on_scrollbar PASSED [ 56%]
tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_pointerdown_guard_exists_in_source PASSED [ 58%]
tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_pointerup_clears_flag_and_triggers_render PASSED [ 60%]
tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragDetection::test_pointercancel_clears_flag PASSED [ 62%]
tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragRenderDuringDrag::test_drag_guard_exists_in_render_scheduler PASSED [ 64%]
tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragRenderDuringDrag::test_drag_path_uses_full_render PASSED [ 66%]
tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragRenderDuringDrag::test_drag_path_sets_programmatic_scroll PASSED [ 68%]
tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragRenderDuringDrag::test_release_uses_same_render_path PASSED [ 70%]
tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF1MeasurementCorruption::test_mf1_multi_segment_turn_raw_heights PASSED [ 72%]
tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF1MeasurementCorruption::test_mf1_buggy_path_inflates_height PASSED [ 75%]
tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF1MeasurementCorruption::test_mf1_queryselector_returns_segment_not_container PASSED [ 77%]
tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF2CrossTypeCollision::test_mf2_user_row_in_assistant_slot_without_guard PASSED [ 79%]
tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF2CrossTypeCollision::test_mf2_assistant_turn_in_user_slot_without_guard PASSED [ 81%]
tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF2CrossTypeCollision::test_mf2_stash_collision_rate_in_shifted_indices PASSED [ 83%]
tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF3TestSensitivity::test_mf3_measurement_test_fails_on_buggy_layout PASSED [ 85%]
tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF3TestSensitivity::test_mf3_type_check_test_fails_on_unguarded_code PASSED [ 87%]
tests/test_issue4346_vscroll_footer_jitter.py::TestMaintainerMF3TestSensitivity::test_mf3_recycle_key_separation_prevents_selector_collision PASSED [ 89%]
tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragFullRender::test_drag_path_calls_compensate_scroll PASSED [ 91%]
tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragFullRender::test_drag_path_sets_programmatic_scroll PASSED [ 93%]
tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragFullRender::test_drag_path_no_spacer_only_update PASSED [ 95%]
tests/test_issue4346_vscroll_footer_jitter.py::TestScrollbarDragFullRender::test_drag_path_updates_window_key PASSED [ 97%]
tests/test_issue4346_vscroll_footer_jitter.py::TestStashKeyCoercion::test_string_dataset_matches_numeric_lookup PASSED [100%]
============================== warnings summary ===============================
tests/test_issue4346_vscroll_footer_jitter.py::test_js_recycle_flag_exists
C:\Apps\hermes\hermes-agent\venv\Lib\site-packages\discord\player.py:30: DeprecationWarning: 'audioop' is deprecated and slated for removal in Python 3.13
import audioop
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
======================== 48 passed, 1 warning in 6.45s ========================