Skip to content

fix: reduce transparent stream live row churn - #5456

Merged
3 commits merged into
nesquena:masterfrom
Stacey2911:pr/transparent-stream-dom-stability-v2
Jul 4, 2026
Merged

3 commits merged into
nesquena:masterfrom
Stacey2911:pr/transparent-stream-dom-stability-v2

Conversation

@Stacey2911

@Stacey2911 Stacey2911 commented Jul 3, 2026 •

Copy link
Copy Markdown
Contributor

Thinking Path

Transparent Stream now preserves live rows by identity, but follow-up testing found remaining no-op churn and layout instability during streaming updates:

  1. preserved rows could still refresh child markup even when the incoming row HTML was unchanged;
  2. preserved rows could still be reinserted even when already in the correct sibling position;
  3. long thinking blocks could still show internal scrollbar flicker once their scroll container appeared.

Those cases preserve the top-level row node, but still create unnecessary DOM/layout instability during live rendering.

What Changed

  • Carries _tcData forward when a transparent live row is reconciled.
  • Keeps copy/header handler rebinds tied to real child markup refreshes.
  • Skips innerHTML replacement when the candidate row markup is unchanged.
  • Skips insertBefore/appendChild when a reconciled row is already under the correct parent and in the correct sibling position.
  • Stabilizes the long thinking-block scroll container/scrollbar behavior.
  • Preserves new-row insertion, moved-row repair, live footer ordering, expanded/detail state, and existing reconciliation behavior.

Why It Matters

This reduces Transparent Stream no-op DOM/layout churn during live responses without changing Compact Worklog behavior or replacing the existing row reconciliation model.

In local browser validation, this removed visible chat-window flicker caused by stable transparent rows being refreshed or reinserted during token generation, and also removed the remaining internal scrollbar flicker inside long thinking blocks.

Contract Routing

Touches Transparent Stream live-row reconciliation and visual/DOM stability. It does not change persisted session shape, message payloads, provider API behavior, backend contracts, or activity mode selection.

Verification

Local:

node --check static/ui.js
node --check static/messages.js
./scripts/test.sh tests/test_issue5367_transparent_live_row_reconcile.py tests/test_issue3820_chat_activity_display_mode.py tests/test_live_to_final_anchor_visible_order.py tests/test_stable_assistant_turn_anchor_registry.py
git diff --check

Result: 122 tests passed locally after the thinking-block scrollbar fix was added.

Browser/manual:

  • Activity Display: Transparent Stream
  • Long streaming response with thinking/tool/activity rows
  • Confirmed stable rows no longer flash/reinsert on every token
  • Confirmed long thinking-block internal scrollbar no longer flickers/jitters
  • Confirmed row ordering and footer placement remain correct
  • Confirmed expand/collapse remains stable

Risks / Follow-ups

  • This intentionally does not include motion/fade suppression changes, because current upstream work is already tracking transparent live-row animation suppression separately.
  • Copy-control parity is kept for a follow-up PR to keep this change focused.
  • Remaining Transparent Stream copy-control work is not included here.

Model Used

Planning/review: OpenAI GPT-5.5 Thinking.

Implementation/review support: Codex CLI and HermesAgent running qwen3.6-27b@q4_k_m.

Manual browser validation was performed by the user on the local HermesWebUI runtime.

@Stacey2911
Stacey2911 marked this pull request as ready for review July 4, 2026 04:00
@greptile-apps

greptile-apps Bot commented Jul 4, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR reduces Transparent Stream live-row DOM churn during token generation through three coordinated changes: a backward-pass insertion algorithm that skips insertBefore/appendChild for rows already in the correct position, a _refreshTransparentThinkingLiveRow fast-path that surgically updates only the <pre> text and preview (preserving the scroll container and scrollTop), and an htmlChanged guard that skips innerHTML replacement for non-thinking rows whose markup is unchanged.

  • Backward positioning pass (_renderLiveAnchorActivitySceneTransparent): rows are collected into targetRenderedRows first, then placed from last to first — skipping DOM mutations for already-positioned stable rows and correctly handling footer ordering and row reordering.
  • Thinking-row fast-path (_refreshTransparentThinkingLiveRow): avoids recreating the scroll container on every token by writing only pre.textContent and calling _decorateTransparentEventRow, which preserves scrollTop and eliminates the scrollbar flicker described in the PR.
  • _tcData forwarding: the candidate's tool-call data is transferred to the preserved node and then deleted from the candidate via try{ delete node._tcData; }catch(_){}, preventing stale data from being re-used in subsequent passes.

Confidence Score: 5/5

Safe to merge — all changes are additive DOM optimization in the Transparent Stream reconciliation path with no changes to message payloads, session shape, or Compact Worklog behavior.

The backward-pass positioning logic is correct and validated by the new insertion-count assertions. The thinking fast-path correctly falls back to full innerHTML replacement for type transitions. The _tcData carry and htmlChanged guard are both well-tested with real ui.js extractions in Node.js.

No files require special attention.

Important Files Changed

Filename Overview
static/ui.js Core reconciliation changes: backward-pass insertion dedup, new thinking fast-path, and conditional innerHTML guard. Logic is sound; one subtle interaction worth noting.
static/style.css Adds scrollbar-gutter:stable to thinking-card-body to reserve scrollbar space and prevent layout shift during content growth.
tests/test_issue5367_transparent_live_row_reconcile.py Adds FakeElement.nextSibling getter, insertion-count instrumentation, and four new focused tests for insertion dedup, HTML-skip, thinking-path, and _tcData carry. Coverage is thorough.
tests/test_issue3820_chat_activity_display_mode.py Updates CSS assertion to include the new scrollbar-gutter:stable property. Minimal, correct change.
tests/test_live_to_final_anchor_visible_order.py Adds eval of _refreshTransparentThinkingLiveRow so the function is available when the full reconciliation pipeline is exercised in this test module.

Reviews (2): Last reviewed commit: "fix: simplify transparent row sibling ch..." | Re-trigger Greptile

Comment thread static/ui.js Outdated
@Stacey2911

Copy link
Copy Markdown
Contributor Author

Addressed the Greptile note about the unreachable transparentLiveRowNextSibling fallback in a small follow-up commit. Local PR-A validation still passes (122 tests, syntax checks, diff-check).

@greptile-apps

greptile-apps Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Want your agent to iterate on Greptile's feedback? Try greploops.

@Stacey2911

Copy link
Copy Markdown
Contributor Author

I checked overlap with #5506 since it also touches Transparent Stream live-row behavior.

The overlap appears limited to the general stable row-ordering/reinsert area in _renderLiveAnchorActivitySceneTransparent(). This PR intentionally avoids the prose fade / motion surface handled by #5506 and keeps the local motion/fade commits out of scope.

The pieces here that still appear distinct are:

  1. skipping unchanged innerHTML refreshes for preserved non-fade rows;
  2. footer-aware stable row positioning, including explicit footer-boundary repair coverage;
  3. long thinking-block scrollbar stability.

If #5506 lands first, I can rebase this branch and keep only the remaining distinct DOM-stability pieces.

@nesquena-hermes nesquena-hermes added the size:L Large PR (>10 files or >250 LOC) label Jul 4, 2026
nesquena-hermes added a commit that referenced this pull request Jul 4, 2026
Stacey2911's follow-up to the identity-preserving live-row reconcile: skip
redundant innerHTML rewrite when unchanged, skip reinsert when already in
position, stop thinking-block scrollbar flicker. Deep-review subagent + gate:
no identity-invariant break (attrs/className/_tcData/interactive-state all still
synced unconditionally; only redundant DOM writes gated). Staged on current
master (resolves the stale-base #4295 test artifact — 114 transparent/anchor
tests green on merge).
@nesquena-hermes nesquena-hermes closed this pull request by merging all changes into nesquena:master in 7274634 Jul 4, 2026
@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Shipped in v0.51.854 🎉 — merged via release stage-5456 (commit on master).

Thanks @Stacey2911 — a clean follow-up to the identity-preserving live-row reconcile. The streaming view is smoother now: no redundant innerHTML rewrites when a row is unchanged, no reinsert when a row is already positioned, and no thinking-block scrollbar flicker.

Cleared the full gate:

  • Codex: SAFE TO SHIP (no findings)
  • Independent deep review: SAFE TO SHIP — verified the skip gates never show stale content (attrs/className/_tcData/interactive-state all still sync unconditionally; only the redundant DOM write is gated) and the position-skip reproduces the exact terminal order without orphaning
  • UX review: SHIP — the reconcile skips are a pure no-op-work reduction (terminal DOM verified identical; the visible difference is the absence of flicker); the scrollbar-gutter:stable line matches four in-repo precedents
  • Full suite green (11970 passed)

Note: the branch predated master's #4295 scroll-anchor fix so the raw head showed 2 unrelated reds — they cleared on merge-forward (114 transparent/anchor tests green on the merged tree). Closing as shipped — thanks for the contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L Large PR (>10 files or >250 LOC)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants