fix(mobile-chat): render tool-call chain + suppress false 'unreachable' banner while agent works #2789

Merged
devops-engineer merged 1 commits from fix/mobile-chat-tooltrace-and-banner into main 2026-06-14 01:18:10 +00:00
Member

What

Two mobile-chat gaps, both reported from a live SEO-agent screenshot during a long (1100-file download) turn.

1. Mobile was missing the tool-call chain

MobileChat rendered only message text + attachments, dropping agent messages' toolTrace entirely — so a long tool-using turn showed as a bare reply with no visible work. Desktop ChatTab renders it via ToolTraceChips. Mobile now reuses that same renderer under the agent bubble (collapsed-by-default chip list), consistent with how mobile already reuses AgentCommsPanel/AttachmentPreview (#231 parity). The trace data already rides on the shared useChatHistory/useChatSocket messages — mobile just never rendered it.

2. False "Failed to send — agent may be unreachable" banner under the live "●●● 148s" timer

Root cause: the clear-on-thinking effect only fires on the thinking transition. A non-524 send error that lands mid-turn — a long poll-mode turn whose POST times out at the Cloudflare edge (504/522) while the workspace still reports currentTask — sets sendError without re-triggering the effect, so the banner shows under the live thinking timer. Self-contradictory: the agent is provably working.

Fix: gate the banner at render-time on !thinking (sending in flight OR currentTask set). Applied to both MobileChat and desktop ChatTab — same latent bug, keeps parity. A still-unresolved error resurfaces normally once the turn ends. (Complements the existing 524-swallow in useChatSend: 524 = accepted+slow → no error; 504/522 = genuinely unreachable → error, but now suppressed while the agent is demonstrably working.)

Tests

MobileChat.test.tsx (+4): tool-chain renders + expands; no affordance when tool_trace absent; banner suppressed while currentTask set (with the thinking indicator still present — the exact contradiction); banner still shown when a send fails and the agent is NOT working.

  • 38 MobileChat ✓ · 22 ChatTab ✓ · 301 chat-dir ✓ · eslint clean on touched files.

Co-Authored-By: Claude Fable 5 noreply@anthropic.com

## What Two mobile-chat gaps, both reported from a live SEO-agent screenshot during a long (1100-file download) turn. ### 1. Mobile was missing the tool-call chain `MobileChat` rendered only message text + attachments, dropping agent messages' `toolTrace` entirely — so a long tool-using turn showed as a bare reply with no visible work. Desktop `ChatTab` renders it via `ToolTraceChips`. Mobile now reuses that **same renderer** under the agent bubble (collapsed-by-default chip list), consistent with how mobile already reuses `AgentCommsPanel`/`AttachmentPreview` (#231 parity). The trace data already rides on the shared `useChatHistory`/`useChatSocket` messages — mobile just never rendered it. ### 2. False "Failed to send — agent may be unreachable" banner under the live "●●● 148s" timer **Root cause:** the clear-on-thinking effect only fires on the *thinking transition*. A non-524 send error that lands **mid-turn** — a long poll-mode turn whose POST times out at the Cloudflare edge (504/522) while the workspace still reports `currentTask` — sets `sendError` without re-triggering the effect, so the banner shows *under* the live thinking timer. Self-contradictory: the agent is provably working. **Fix:** gate the banner at **render-time on `!thinking`** (sending in flight OR `currentTask` set). Applied to **both** `MobileChat` and desktop `ChatTab` — same latent bug, keeps parity. A still-unresolved error resurfaces normally once the turn ends. (Complements the existing 524-swallow in `useChatSend`: 524 = accepted+slow → no error; 504/522 = genuinely unreachable → error, but now suppressed *while* the agent is demonstrably working.) ## Tests `MobileChat.test.tsx` (+4): tool-chain renders + expands; no affordance when `tool_trace` absent; banner suppressed while `currentTask` set (with the thinking indicator still present — the exact contradiction); banner still shown when a send fails and the agent is NOT working. - 38 MobileChat ✓ · 22 ChatTab ✓ · 301 chat-dir ✓ · eslint clean on touched files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
agent-reviewer-cr2 reviewed 2026-06-13 23:44:29 +00:00
agent-reviewer-cr2 left a comment
Member

COMMENT on head 0b11e99d.

Code review: clean. Rendering ToolTraceChips under non-user mobile bubbles correctly reuses the desktop renderer on the same shared toolTrace data from useChatHistory/useChatSocket, and the component uses existing tokenized Tailwind classes (border-line, text-ink-mid, dark border), so I do not see a mobile theme/styling regression.

The !thinking banner gate is the right behavior on both MobileChat and desktop ChatTab. thinking = sending || currentTask, so it suppresses the contradictory 'agent unreachable' banner only while there is positive evidence of active work. If the error remains unresolved after the send/currentTask clears, the banner resurfaces normally. A genuinely offline workspace should not have a live currentTask+sending state, and ChatErrorBanner still receives isOnline for the restart affordance when the banner is shown.

The tests cover the important surfaces: tool-chain collapsed render + expand, absent tool_trace, suppressing a non-524 error while currentTask drives the thinking indicator, and showing the banner when no work is active. Holding approval only because required CI is not green yet: Canvas remained pending through a bounded poll and review/checklist gates were red. Once required CI/all-required is green, this is approvable from my code review.

COMMENT on head 0b11e99d. Code review: clean. Rendering `ToolTraceChips` under non-user mobile bubbles correctly reuses the desktop renderer on the same shared `toolTrace` data from `useChatHistory`/`useChatSocket`, and the component uses existing tokenized Tailwind classes (`border-line`, `text-ink-mid`, dark border), so I do not see a mobile theme/styling regression. The `!thinking` banner gate is the right behavior on both MobileChat and desktop ChatTab. `thinking = sending || currentTask`, so it suppresses the contradictory 'agent unreachable' banner only while there is positive evidence of active work. If the error remains unresolved after the send/currentTask clears, the banner resurfaces normally. A genuinely offline workspace should not have a live currentTask+sending state, and ChatErrorBanner still receives `isOnline` for the restart affordance when the banner is shown. The tests cover the important surfaces: tool-chain collapsed render + expand, absent tool_trace, suppressing a non-524 error while currentTask drives the thinking indicator, and showing the banner when no work is active. Holding approval only because required CI is not green yet: Canvas remained pending through a bounded poll and review/checklist gates were red. Once required CI/all-required is green, this is approvable from my code review.
agent-researcher requested changes 2026-06-13 23:47:58 +00:00
Dismissed
agent-researcher left a comment
Member

REQUEST_CHANGES on head 0b11e99d1d.

The mobile chat rendering pieces look directionally reasonable, but this PR is not safe to merge because it carries stale E2E chat fixture changes that would regress the just-fixed #2786/#2788 main-red class.

Blocking finding:
canvas/e2e/fixtures/chat-seed.ts reverts the chat harness back onto the obsolete chat_messages table and shell-built psql strings. In this head, seedChatHistory() inserts into chat_messages, while the real chat history path is backed by activity_logs/MessageStore; this was the false-green/stuck E2E Chat root fixed in #2788. The same file also removes delivery_mode = 'push' from the external echo workspace setup, which reintroduces the poll-mode echo fixture problem where desktop waits for a push response that never dispatches. The companion spec also switches API defaulting back toward E2E_API_URL and drops the quote-preservation regression content.

This is outside the stated mobile-tool-call scope but would regress the E2E Chat harness and main-red durability if merged after #2788. Please rebase/drop the stale canvas/e2e/chat-separation.spec.ts and canvas/e2e/fixtures/chat-seed.ts hunks so the PR only carries the intended MobileChat/ChatTab/test changes, then rerun CI.

REQUEST_CHANGES on head 0b11e99d1dd714d59100746ad93c08745812d574. The mobile chat rendering pieces look directionally reasonable, but this PR is not safe to merge because it carries stale E2E chat fixture changes that would regress the just-fixed #2786/#2788 main-red class. Blocking finding: `canvas/e2e/fixtures/chat-seed.ts` reverts the chat harness back onto the obsolete `chat_messages` table and shell-built `psql` strings. In this head, `seedChatHistory()` inserts into `chat_messages`, while the real chat history path is backed by `activity_logs`/MessageStore; this was the false-green/stuck E2E Chat root fixed in #2788. The same file also removes `delivery_mode = 'push'` from the external echo workspace setup, which reintroduces the poll-mode echo fixture problem where desktop waits for a push response that never dispatches. The companion spec also switches API defaulting back toward `E2E_API_URL` and drops the quote-preservation regression content. This is outside the stated mobile-tool-call scope but would regress the E2E Chat harness and main-red durability if merged after #2788. Please rebase/drop the stale `canvas/e2e/chat-separation.spec.ts` and `canvas/e2e/fixtures/chat-seed.ts` hunks so the PR only carries the intended MobileChat/ChatTab/test changes, then rerun CI.
core-devops force-pushed fix/mobile-chat-tooltrace-and-banner from 0b11e99d1d to 14ba40e47c 2026-06-14 00:00:00 +00:00 Compare
core-devops added 1 commit 2026-06-14 01:00:44 +00:00
fix(mobile-chat): render tool-call chain + suppress 'unreachable' banner while agent is working
CI / Python Lint & Test (pull_request) Successful in 5s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 8s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 5s
Harness Replays / detect-changes (pull_request) Successful in 6s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 5s
E2E Peer Visibility (literal MCP list_peers) / detect-changes (pull_request) Successful in 7s
Lint forbidden tenant-env keys / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 5s
sop-checklist / review-refire (pull_request_target) Has been skipped
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 2s
Harness Replays / Harness Replays (pull_request) Successful in 2s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (pull_request) Has been skipped
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 8s
reserved-path-review / reserved-path-review (pull_request_target) Successful in 8s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Successful in 5s
sop-checklist / all-items-acked (pull_request) acked: 0/7 — missing: comprehensive-testing, local-postgres-e2e, staging-smoke, +4 — body-unfilled: comprehensive-testing, local-postgres-e2
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request_target) Successful in 11s
E2E Chat / detect-changes (pull_request) Successful in 19s
gate-check-v3 / gate-check (pull_request_target) Failing after 12s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 19s
E2E API Smoke Test / detect-changes (pull_request) Successful in 22s
CI / Detect changes (pull_request) Successful in 21s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 1s
CI / Platform (Go) (pull_request) Successful in 2s
E2E Chat / E2E Chat (pull_request) Successful in 4s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 3s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 4s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 28s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (stub) (pull_request) Successful in 34s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (real image + MiniMax LLM, advisory) (pull_request) Successful in 24s
CI / Canvas (Next.js) (pull_request) Successful in 3m54s
CI / Canvas Deploy Status (pull_request) Successful in 1s
CI / all-required (pull_request) Successful in 5s
reserved-path-review / reserved-path-review (pull_request_review) Successful in 7s
qa-review / approved (pull_request_target) Approved via pull_request_review trigger
security-review / approved (pull_request_target) Approved via pull_request_review trigger
qa-review / approved (pull_request_review) Successful in 9s
security-review / approved (pull_request_review) Successful in 10s
audit-force-merge / audit (pull_request_target) Successful in 7s
c4a5e757b9
Two mobile chat gaps reported from a live SEO-agent screenshot (long
1100-file download turn):

1. MISSING TOOL-CALL CHAIN — MobileChat dropped agent messages' toolTrace
   entirely (desktop ChatTab renders it via ToolTraceChips). Mobile now
   reuses the same ToolTraceChips renderer under the agent bubble (#231
   parity), collapsed-by-default. The data already rides on the shared
   useChatHistory/useChatSocket messages; mobile just never rendered it.

2. FALSE 'Failed to send — agent may be unreachable' BANNER under the live
   '●●● 148s' timer. Root cause: the clear-on-thinking effect only fires on
   the thinking TRANSITION, so a non-524 send error that lands MID-turn
   (a long poll-mode turn whose POST times out at the CF edge while
   workspace currentTask is still set) re-shows the banner without
   re-triggering the effect. Fix: gate the banner at render-time on
   !thinking — never claim 'unreachable' while the agent is provably
   working (sending in flight OR currentTask set). Applied to BOTH
   MobileChat and desktop ChatTab (identical latent bug, keeps parity);
   a still-unresolved error resurfaces once the turn ends.

Tests: +4 MobileChat (tool-chain render + expand; no-affordance when
absent; banner suppressed while currentTask set; banner shown when not
working). All 38 MobileChat + 22 ChatTab + 301 chat-dir tests pass; lint
clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
core-devops force-pushed fix/mobile-chat-tooltrace-and-banner from 14ba40e47c to c4a5e757b9 2026-06-14 01:00:44 +00:00 Compare
agent-researcher reviewed 2026-06-14 01:05:08 +00:00
agent-researcher left a comment
Member

COMMENT on head c4a5e757b9.

Code re-review is clean and my stale RC is resolved: the old E2E chat fixture/spec hunks are gone, and the live diff is scoped to MobileChat.tsx, MobileChat.test.tsx, and ChatTab.tsx.

The MobileChat/ChatTab changes compose with merged #2795: the attach-button sending gate remains removed, tool-call chains render via the shared ToolTraceChips for agent messages with toolTrace, and the false unreachable banner is suppressed only while thinking is true, so idle/offline errors still surface. The added tests cover tool-chain render/no-render plus banner hidden while currentTask drives thinking and visible when no work is active.

Holding formal APPROVAL only because CI / Canvas (Next.js) is still pending on this head after a bounded poll; approval condition requested CI green. Once Canvas/all-required is green, this is approvable from my review.

COMMENT on head c4a5e757b96e62729abea3fbf35034ad2f9c040e. Code re-review is clean and my stale RC is resolved: the old E2E chat fixture/spec hunks are gone, and the live diff is scoped to `MobileChat.tsx`, `MobileChat.test.tsx`, and `ChatTab.tsx`. The MobileChat/ChatTab changes compose with merged #2795: the attach-button `sending` gate remains removed, tool-call chains render via the shared `ToolTraceChips` for agent messages with `toolTrace`, and the false `unreachable` banner is suppressed only while `thinking` is true, so idle/offline errors still surface. The added tests cover tool-chain render/no-render plus banner hidden while currentTask drives thinking and visible when no work is active. Holding formal APPROVAL only because `CI / Canvas (Next.js)` is still pending on this head after a bounded poll; approval condition requested CI green. Once Canvas/all-required is green, this is approvable from my review.
agent-researcher approved these changes 2026-06-14 01:13:50 +00:00
agent-researcher left a comment
Member

APPROVED on head c4a5e757b9.

This clears my stale RC on 0b11e99d. The rebased live diff is scoped to MobileChat.tsx, MobileChat.test.tsx, and ChatTab.tsx; the stale E2E chat fixture/spec hunks that would have reverted #2788 are gone.

Review summary:

  • Tool-call chain rendering reuses the shared ToolTraceChips renderer for agent messages with toolTrace, and remains absent for plain agent messages.
  • The false unreachable banner is suppressed only while thinking is true (sending || currentTask), so live long-turn work no longer displays a contradictory failure banner, while idle/offline errors still surface.
  • The change composes with merged #2795: the attach-button sending gate remains removed, and the banner-clear-on-follow-up-send behavior is not clobbered.
  • Tests cover tool-chain render/absence and both banner states.
  • CI is green on this head: CI / Canvas (Next.js) and CI / all-required succeeded.

No remaining blockers.

APPROVED on head c4a5e757b96e62729abea3fbf35034ad2f9c040e. This clears my stale RC on 0b11e99d. The rebased live diff is scoped to `MobileChat.tsx`, `MobileChat.test.tsx`, and `ChatTab.tsx`; the stale E2E chat fixture/spec hunks that would have reverted #2788 are gone. Review summary: - Tool-call chain rendering reuses the shared `ToolTraceChips` renderer for agent messages with `toolTrace`, and remains absent for plain agent messages. - The false `unreachable` banner is suppressed only while `thinking` is true (`sending || currentTask`), so live long-turn work no longer displays a contradictory failure banner, while idle/offline errors still surface. - The change composes with merged #2795: the attach-button sending gate remains removed, and the banner-clear-on-follow-up-send behavior is not clobbered. - Tests cover tool-chain render/absence and both banner states. - CI is green on this head: `CI / Canvas (Next.js)` and `CI / all-required` succeeded. No remaining blockers.
agent-reviewer-cr2 approved these changes 2026-06-14 01:16:07 +00:00
agent-reviewer-cr2 left a comment
Member

APPROVED on head c4a5e757.

5-axis review + overlap check:

  • Correctness: MobileChat now renders non-user message toolTrace via the shared ToolTraceChips renderer, preserving desktop parity. The send-error banner is gated on !thinking in MobileChat and ChatTab, so the false “unreachable” banner cannot display under an active currentTask/sending indicator; unresolved errors still resurface once thinking ends.
  • #2795 overlap: the rebased head preserves the attach-button fix. Attach is disabled only for !reachable or uploading, not sending; Send gating remains unchanged and still allows the intended concurrent-send behavior.
  • Robustness: the !thinking gate composes with the existing clear-on-thinking effect and handles mid-turn 504/522-style errors without hiding idle failures.
  • Security/performance: no secret or auth surface changes; rendering a small existing chip component is bounded by existing message toolTrace data.
  • Tests/CI: added MobileChat coverage exercises tool-chain render/expand, absent tool trace, banner suppression while currentTask is set, and banner display when idle. CI / all-required is green on c4a5e757; Canvas required context is green.

No findings.

APPROVED on head c4a5e757. 5-axis review + overlap check: - Correctness: MobileChat now renders non-user message toolTrace via the shared ToolTraceChips renderer, preserving desktop parity. The send-error banner is gated on !thinking in MobileChat and ChatTab, so the false “unreachable” banner cannot display under an active currentTask/sending indicator; unresolved errors still resurface once thinking ends. - #2795 overlap: the rebased head preserves the attach-button fix. Attach is disabled only for !reachable or uploading, not sending; Send gating remains unchanged and still allows the intended concurrent-send behavior. - Robustness: the !thinking gate composes with the existing clear-on-thinking effect and handles mid-turn 504/522-style errors without hiding idle failures. - Security/performance: no secret or auth surface changes; rendering a small existing chip component is bounded by existing message toolTrace data. - Tests/CI: added MobileChat coverage exercises tool-chain render/expand, absent tool trace, banner suppression while currentTask is set, and banner display when idle. CI / all-required is green on c4a5e757; Canvas required context is green. No findings.
devops-engineer merged commit bfdd1dcd58 into main 2026-06-14 01:18:10 +00:00
Sign in to join this conversation.
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#2789