fix(ui): skip chat history reload during active sends to prevent mess…#66997
Conversation
Greptile SummaryAdds an early-return guard in Confidence Score: 5/5Safe to merge — single-condition guard with correct timing semantics and no behavioral regressions. The only finding is a P2 suggestion to add a regression test; all logic is correct and the fix is well-reasoned. A missing test does not block merge. No files require special attention. Prompt To Fix All With AIThis is a comment left during a code review.
Path: ui/src/ui/app-gateway.ts
Line: 425-432
Comment:
**Missing regression test for the guard**
The existing `app-gateway.sessions.node.test.ts` already has a `describe("handleGatewayEvent session.message", ...)` block with mock infrastructure wired up. A third test case locking in the new guard would be trivial to add and would prevent this class of regression from going undetected again. The PR description flags this as a known gap and proposes a follow-up — the test infrastructure already exists, so this is the right place for it.
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "fix(ui): skip chat history reload during..." | Re-trigger Greptile |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aecf83916e
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 46083ddd0b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…age card delay When a user sends a message, the Control UI performs an optimistic update to display the message immediately. However, if a session.message gateway event arrives during the send, it triggers an unconditional loadChatHistory() call. This race condition resets chatStream to null, causing the optimistic message card to disappear until the first LLM delta arrives. The fix adds a guard to skip history reload while a chat run is active (host.chatRunId is set). The chat event handler already manages streaming state and appends the final assistant message, making the concurrent reload unnecessary during active sends. External message refresh is preserved for idle sessions. Fixes the regression introduced in commit 20266c1 (added session.message event handling for qa-lab testing). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… chat run Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
46083dd to
cec28cf
Compare
|
Merged via squash.
Thanks @scotthuang! |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cec28cfa90
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (deferredSessionKey && payloadSessionKey && deferredSessionKey === payloadSessionKey) { | ||
| deferredReloadHost.pendingSessionMessageReloadSessionKey = null; | ||
| } |
There was a problem hiding this comment.
Preserve deferred reload state until terminal event
Fresh evidence in this revision: this new reset path clears pendingSessionMessageReloadSessionKey on any same-session chat event, including delta. When a session.message is deferred during an active run and the run later emits one or more deltas before ending in aborted/error, the deferred key is nulled before terminal handling, so shouldReplayDeferredSessionMessageReload is false and loadChatHistory() is never replayed; concurrent external messages can remain missing until an unrelated refresh.
Useful? React with 👍 / 👎.
openclaw#66997) Merged via squash. Prepared head SHA: cec28cf Co-authored-by: scotthuang <1670837+scotthuang@users.noreply.github.com> Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com> Reviewed-by: @vincentkoc
openclaw#66997) Merged via squash. Prepared head SHA: cec28cf Co-authored-by: scotthuang <1670837+scotthuang@users.noreply.github.com> Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com> Reviewed-by: @vincentkoc
openclaw#66997) Merged via squash. Prepared head SHA: cec28cf Co-authored-by: scotthuang <1670837+scotthuang@users.noreply.github.com> Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com> Reviewed-by: @vincentkoc
Summary
handleSessionMessageGatewayEvent()to skiploadChatHistory()while a chat run is active (host.chatRunIdis set).Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
20266c14cb(addedsession.messageevent handling for qa-lab testing)Root Cause (if applicable)
20266c14cbadded asession.messagegateway event handler that unconditionally callsloadChatHistory(). When a user sends a message, the optimistic update appends the user message card immediately. However, the gateway broadcasts asession.messageevent for the same message, triggeringloadChatHistory()which resetschatStreamtonull. This clears the streaming state before the first LLM delta arrives, causing the optimistic message card to disappear.session.messageevents.session.messageevent was added to support qa-lab external message injection testing. The race condition only manifests when the event arrives during the narrow window between the optimistic update and the first LLM streaming delta.Regression Test Plan (if applicable)
ui/src/ui/app-gateway.test.ts(if exists) or a new test forhandleSessionMessageGatewayEventhost.chatRunIdis set (active send), asession.messageevent for the same session should NOT triggerloadChatHistory().User-visible / Behavior Changes
Diagram (if applicable)
Security Impact (required)
Repro + Verification
Environment
gateway.mode=localSteps
http://127.0.0.1:18789Expected
Actual
Evidence
Human Verification (required)
chatRunIdset).Review Conversations
Compatibility / Migration
Trade-off Analysis
This is the highest cost-effectiveness fix for this regression: a single
ifguard, zero refactoring, zero risk to existing event handling.The only theoretical downside is: if a user is actively sending a message and an external message arrives in the same session at the exact same time, the external message won't appear until the current chat run completes and history reloads. This is an extremely narrow concurrent-input window that is unlikely in normal usage. Once the run finishes,
loadChatHistory()is called by the terminal event handler, so the external message appears shortly after regardless.A "perfect" fix would require splitting
loadChatHistory()into an additive append path that merges new messages without resetting streaming state — significantly more invasive for a marginal edge case.Risks and Mitigations