Skip to content

Fix/UI session history sync#1633

Open
BingqingLyu wants to merge 5 commits intomainfrom
fork-pr-57077-fix-ui-session-history-sync
Open

Fix/UI session history sync#1633
BingqingLyu wants to merge 5 commits intomainfrom
fork-pr-57077-fix-ui-session-history-sync

Conversation

@BingqingLyu
Copy link
Copy Markdown
Owner

@BingqingLyu BingqingLyu commented Apr 27, 2026

Summary

Describe the problem and fix in 2–5 bullets:

  • Problem: The Chat session picker could display a stale session after navigation or deep-link rehydration even while the loaded thread content was already for the correct session.
  • Why it matters: Users could see mismatched session metadata in the UI and lose trust in which thread they were reading or about to send into.
  • What changed: The session picker now explicitly marks the active option as selected during rerender, and the hidden mobile picker no longer reuses the desktop session-control class.
  • What changed: Added regression coverage for desktop and mobile session pickers when the current session is injected as a fallback option after rerender.
  • What did NOT change (scope boundary): No gateway protocol/API changes, no session-store schema changes, no auth/token behavior changes, no navigation behavior changes, and no chat-history loading logic changes.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause / Regression History (if applicable)

  • Root cause:
    1. The chat session <select> relied on .value while its option list could be rebuilt during rerender.
    2. When the active session only existed via fallback option injection, the browser could keep showing a previously selected option instead of the current session.
    3. The hidden mobile picker reused the same chat-controls__session class as the desktop picker, which made selector targeting ambiguous.
  • Missing detection / guardrail: No regression test covered rerendering into a fallback session option, and no test distinguished the desktop and mobile session pickers.
  • Prior context (git blame, prior PR, issue, or refactor if known): Earlier investigation focused on async history/nav flows because the symptom looked like session-state drift; the exact repro showed the thread content was already correct while only the selector was stale.
  • Why this regressed now: The mismatch becomes visible when Chat rerenders after navigation or when opening /chat?session=... and the session options are reconstructed around the current session.
  • If unknown, what was ruled out: Not a gateway storage corruption issue, not a chat.history overwrite, and not a session reset caused by auth/token handling.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file:
    • ui/src/ui/views/chat.test.ts
  • Scenario the test should lock in:
    • The desktop session picker must keep the current session selected after rerendering to a fallback session option.
    • The mobile session picker must keep the current session selected after rerendering to a fallback session option.
  • Why this is the smallest reliable guardrail: The bug is in selector rendering, so a focused UI unit test covers the exact failure mode without depending on gateway, navigation, or async history timing.
  • Existing test that already covers this (if any): Existing grouped-session selector tests already covered option labeling/fallback generation but not selected-option persistence.
  • If no new test is added, why not: N/A.

User-visible / Behavior Changes

  • The visible Chat session picker stays aligned with the currently loaded thread after navigation or deep-link rehydration.
  • Desktop and mobile session pickers are now distinguishable in the DOM.
  • No intended change to chat history loading, session routing, or sidebar navigation behavior.

Diagram (if applicable)

Before:
[session = test-b] -> [session options rerender]
-> [fallback option injected]
-> [picker can still show old selected option test-e]
-> [thread content remains test-b]

After:
[session = test-b] -> [session options rerender]
-> [current option explicitly marked selected]
-> [desktop/mobile pickers are distinct]
-> [picker and thread content both show test-b]

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: Linux (dev workspace)
  • Runtime/container: Node 22.x
  • Model/provider: N/A
  • Integration/channel (if any): Control UI chat session selector
  • Relevant config (redacted): Control UI loaded with a non-default session query param or after returning to Chat with a previously selected session

Steps

  1. Open Control UI Chat with a non-default session, for example /chat?session=agent%3Amain%3Atest-b.
  2. Trigger a rerender or re-hydration path that rebuilds the session options.
  3. Observe the selected session in the picker and the rendered chat thread.

Expected

  • The selected session in the picker and the rendered chat thread match the same session.

Actual

  • Previously: the picker could still show a stale session such as test-e while the rendered thread was already test-b.
  • With this PR: the picker and rendered thread stay aligned.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • [x ] Screenshot/recording
  • Perf numbers (if relevant)

Local verification:

  • pnpm test -- ui/src/ui/views/chat.test.ts ui/src/ui/controllers/chat.test.ts
  • Manual verification: selected a non-default session, navigated away from Chat, returned to Chat, and confirmed the session picker stayed aligned with the loaded thread content across repeated page switches.

before
image
With this PR:
image

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios:
    • Read through the Chat session-picker render path and reduced the branch to a selector-only fix.
    • Added regression coverage for desktop and mobile picker rerender paths where the active session is a fallback option.
    • Ran pnpm test -- ui/src/ui/views/chat.test.ts ui/src/ui/controllers/chat.test.ts.
    • Manually switched between Chat and other pages multiple times with a non-default session selected and confirmed the picker and rendered thread stayed in sync.
  • Edge cases checked:
    • Desktop picker rerender with a newly active fallback session.
    • Mobile picker rerender with a newly active fallback session.
    • Returning to Chat after navigating away with a non-default session selected.
    • Opening Chat directly with a session query param and verifying repeated page switches do not desync the picker.
  • What you did not verify:
    • Full browser end-to-end automation coverage after the branch cleanup.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: Browser <select> behavior can be sensitive when options are rebuilt dynamically.
    • Mitigation: The active session is now explicitly marked selected on rerender, and regression tests cover both desktop and mobile pickers.
  • Risk: DOM-targeting code or tests could still hit the wrong picker if selectors stay ambiguous.
    • Mitigation: The hidden mobile picker now uses a distinct class and explicit data-chat-session-select markers.

bbddbb1 added 5 commits April 5, 2026 16:44
Fix P2 Badge: The mobile picker now uses .selected=${live(...)} instead of
?selected attribute binding to reliably force the live selected option
after rerender, preventing stale selection display when state.sessionKey
changes externally (navigation/deep-link rehydration).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Chat UI State Desync: Rendered messages do not match active session after route navigation

2 participants