Skip to content

fix(ui): prevent false busy state in Control UI webchat#91606

Closed
bladin wants to merge 1 commit into
openclaw:mainfrom
bladin:fix/87387-control-ui-webchat-false-busy-state
Closed

fix(ui): prevent false busy state in Control UI webchat#91606
bladin wants to merge 1 commit into
openclaw:mainfrom
bladin:fix/87387-control-ui-webchat-false-busy-state

Conversation

@bladin

@bladin bladin commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #87387: Control UI webchat could keep showing a false "In progress" status and "Stop" button after the assistant response completed because the main composer consumed session-list abortability even when local send/stream state was idle.

Changes

  • Split session-scoped abortability from main composer busy UI in ui/src/ui/views/chat.ts.
  • Keep the main badge, queue steer affordance, and Stop button tied to local active evidence (sending || stream !== null).
  • Add unit regression coverage for stale active session rows after completion, while preserving Stop/Steer during an active local stream.
  • Add mocked Chromium Control UI proof for send -> final stream -> stale active session row -> idle composer.

Real behavior proof

Behavior addressed: Control UI webchat no longer shows a false In progress badge, Stop button, or queue steer affordance after response completion when only stale session-list active-run state remains.

Real environment tested: Local OpenClaw checkout on rebased PR head f3d079672376a36eee90452d2a6f7d947d87234c, mocked Control UI Gateway E2E in Chromium, Node/Vitest through scripts/run-vitest.mjs.

Exact steps or command run after this patch:

node scripts/run-vitest.mjs ui/src/ui/views/chat.test.ts ui/src/ui/app-chat.test.ts ui/src/ui/app-gateway.sessions.node.test.ts ui/src/ui/chat/run-controls.test.ts
node scripts/run-vitest.mjs run --config test/vitest/vitest.ui-e2e.config.ts --configLoader runner ui/src/ui/e2e/chat-flow.e2e.test.ts
git diff --check

Evidence after fix:

Test Files  4 passed (4)
Tests       137 passed (137)

Test Files  1 passed (1)
Tests       2 passed (2)

git diff --check: clean

Observed result after fix: Unit regression fails on the old behavior, then passes with the split. The mocked browser flow sends a WebChat message, receives the final assistant event, waits for the done status to clear, injects stale sessions.changed active-run state, and confirms no In progress, no Stop generating, no Queue message, with the composer back on Send message.

What was not tested: Broad remote pnpm check:changed did not run locally. The delegated Testbox path failed before execution because .github/workflows/crabbox-hydrate.yml does not contain a Testbox step, and direct AWS Crabbox is not configured in this environment (crabbox login --url https://crabbox.openclaw.ai --provider aws required). Fresh PR CI was triggered by the push to f3d079672376a36eee90452d2a6f7d947d87234c.

AI Assistance

This PR was originally created with AI assistance (iCodemate). Maintainer follow-up added regression and mocked browser proof.

- Added showLocalAbortableUi to gate busy UI on actual stream activity
- Only show in-progress badge and Stop button when there is active stream or sending
- Added e2e test for stale session state not forcing busy UI
- Added unit tests for idle composer with stale session state and active stream

Fixes openclaw#87387
@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui size: S proof: supplied External PR includes structured after-fix real behavior proof. labels Jun 9, 2026
@clawsweeper

clawsweeper Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 30, 2026, 4:53 PM ET / 20:53 UTC.

Summary
The PR narrows Control UI WebChat busy, Stop, Queue, and Steer rendering to sending || stream !== null and adds unit plus mocked Chromium regressions for stale active session rows.

PR surface: Source +3, Tests +128. Total +131 across 3 files.

Reproducibility: yes. at source level: current main still lets combined hasAbortableSessionRun(state) feed the visible composer badge and Stop/Queue/Steer controls. I did not run the exact Windows Chrome plus WSL2 live reproduction in this read-only review.

Review metrics: 1 noteworthy metric.

  • Visible abort/steer gate: 1 gate narrowed across 3 consumers. The boolean feeds composer status, queue steering, and run controls, so maintainers need proof for both stale-idle and active-run states.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #87387
Summary: This PR is the active open fix candidate for the canonical false Control UI WebChat busy/Stop report; broader stuck-run and Stop-recovery work overlaps but does not replace it.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🧂 unranked krab
Result: blocked until real behavior proof from a real setup is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Repair the active-run gate so local chatRunId phases without visible stream still keep Stop, Queue, and Steer controls available.
  • Rebase onto current main and refresh review against the resolved diff.
  • Attach redacted real Control UI browser/gateway proof; redact private details such as IPs, API keys, phone numbers, and non-public endpoints.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body supplies unit output and mocked Chromium Gateway E2E only; add redacted real Control UI browser/gateway proof, then update the PR body or ask for @clawsweeper re-review.

Proof path suggestion
A short browser proof would materially show the fixed stale-idle state and the preserved active-run controls. Mantis is currently scoped to Telegram, Discord, and web UI chat proof, so it is not the right proof path for this surface.
Use maintainer screenshot/manual proof, browser or Playwright proof, Crabbox where appropriate, or normal local artifact proof instead.

Risk before merge

  • [P1] The narrowed gate can suppress Stop, Queue, and Steer while a local chatRunId is still active but chatStream is null, changing how users control and steer queued messages during a live run.
  • [P1] The PR is currently conflicting against main, so maintainers need a rebase and review refresh before trusting the final merge result.
  • [P1] The supplied proof is unit plus mocked Chromium Gateway E2E output, not redacted real Control UI browser/gateway behavior on the repaired head.

Maintainer options:

  1. Preserve local active-run controls (recommended)
    Before merge, carry a local active-run signal such as chatRunId into the abort/steer gate while keeping session-list-only stale rows idle.
  2. Refresh branch and proof together
    After rebasing, require redacted real Control UI browser/gateway evidence for both completed-response idle state and active-run Stop/Steer behavior.
  3. Pause if another fix lands
    If a different merged PR resolves the canonical false-busy issue, close this branch instead of carrying duplicate repair work.

Next step before merge

  • [P1] The next action is contributor-visible branch repair, rebase, and real behavior proof; automation should not take over while the external proof gate is mock-only.

Security
Cleared: The diff only changes Control UI rendering and tests; it does not touch dependencies, workflows, credentials, package metadata, or other code-execution paths.

Review findings

  • [P1] Keep local active runs abortable — ui/src/ui/views/chat.ts:1573
Review details

Best possible solution:

Rebase and repair the Control UI split so session-list-only stale rows do not drive composer busy UI, while genuine local active runs still keep Stop, Queue, and Steer available; then provide redacted real browser/gateway proof.

Do we have a high-confidence way to reproduce the issue?

Yes, at source level: current main still lets combined hasAbortableSessionRun(state) feed the visible composer badge and Stop/Queue/Steer controls. I did not run the exact Windows Chrome plus WSL2 live reproduction in this read-only review.

Is this the best way to solve the issue?

No. Splitting session-list-only abortability from visible composer state is the right direction, but sending || stream !== null is too narrow because local chatRunId can remain active while chatStream is null.

Full review comments:

  • [P1] Keep local active runs abortable — ui/src/ui/views/chat.ts:1573
    The new gate treats only sending or a non-null stream as local active evidence, but hasAbortableSessionRun is also true for a local chatRunId; tool-stream handling can clear chatStream while that run id remains active. Because this narrowed value feeds Stop, Queue, and Steer, active runs can lose their abort/steer controls. Preserve a local run-id signal while excluding session-list-only stale rows.
    Confidence: 0.91

Overall correctness: patch is incorrect
Overall confidence: 0.91

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 9f07b21e6231.

Label changes

Label justifications:

  • P2: This is a normal-priority Control UI state bug with bounded blast radius, but the current PR still has a blocking user-visible control regression.
  • merge-risk: 🚨 message-delivery: The narrowed abortability gate can suppress Queue/Steer controls during a still-active local run, changing how queued user messages are delivered into the active conversation.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body supplies unit output and mocked Chromium Gateway E2E only; add redacted real Control UI browser/gateway proof, then update the PR body or ask for @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +3, Tests +128. Total +131 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 1 6 3 +3
Tests 2 128 0 +128
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 134 3 +131

What I checked:

  • Repository policy read: Root AGENTS.md and ui/AGENTS.md were read fully; the root PR review-depth, proof, and merge-risk guidance affected this review, while the scoped UI i18n rules were not directly relevant. (AGENTS.md:1, 9f07b21e6231)
  • PR diff narrows abort UI: The PR introduces showLocalAbortableUi = showAbortableUi && isBusy, where isBusy is only sending || stream !== null. (ui/src/ui/views/chat.ts:1573, 9ac52a86f974)
  • Local run id is abortable state: The unchanged abortability helper returns true for a local chatRunId before falling back to active session-list rows, so a view-layer split must preserve local run-id state while excluding session-list-only stale rows. (ui/src/ui/app-chat.ts:214, 9ac52a86f974)
  • Current main still has the central issue: Current main still derives visible composer in-progress state from combined abortability, so the requested false-busy fix is not already implemented on main. (ui/src/ui/views/chat.ts:2093, 9f07b21e6231)
  • Run controls depend on abortability: The Stop and Queue buttons are rendered only from props.canAbort, so narrowing the value can remove active-run controls. (ui/src/ui/chat/run-controls.ts:52, 9ac52a86f974)
  • Queue steering depends on abortability: The Steer action is also rendered only when queue props receive canAbort, so the narrowed value can affect queued-message handling. (ui/src/ui/chat/chat-queue.ts:72, 9ac52a86f974)

Likely related people:

  • vincentkoc: Recent GitHub path history shows repeated Control UI chat work around views/chat.ts, app-chat.ts, first-send responsiveness, and web chat rendering. (role: recent area contributor; confidence: high; commits: f5482efbd75e, b3c946999d71, 099abea0897e; files: ui/src/ui/views/chat.ts, ui/src/ui/app-chat.ts, ui/src/ui/app-render.ts)
  • steipete: History shows work on composer controls, queued-message steering, and Control UI source documentation in the affected Stop/Queue/Steer surface. (role: feature-history contributor; confidence: high; commits: 2b30951b8090, 1a8a6f8fba04, 12a56d4d46dd; files: ui/src/ui/chat/run-controls.ts, ui/src/ui/chat/chat-queue.ts, ui/src/ui/views/chat.ts)
  • BunsDev: History includes Control UI run-status cleanup, composer usability, and reconnect queue behavior in adjacent chat lifecycle and rendering paths. (role: adjacent lifecycle and UI contributor; confidence: medium; commits: 4935e24c7a7f, 2ac011b8ae96, 96635c7c27e3; files: ui/src/ui/views/chat.ts, ui/src/ui/chat/run-controls.ts, ui/src/ui/app-chat.ts)
  • osolmaz: Authored the visible chat stream preservation work that makes chatRunId, chatStream, and chatStreamSegments relevant to active tool-run rendering. (role: stream reconciliation contributor; confidence: medium; commits: 17a285f29825; files: ui/src/ui/app-tool-stream.ts, ui/src/ui/controllers/chat.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (1 earlier review cycle)
  • reviewed 2026-06-21T19:01:32.971Z sha 9ac52a8 :: needs real behavior proof before merge. :: [P1] Keep local active runs abortable

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

Labels

app: web-ui App: web-ui merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P2 Normal backlog priority with limited blast radius. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: S status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Control UI webchat can leave false "In progress" / "Stop" state after completed response

1 participant