Skip to content

Show a queued chat message as pending, not as sent - #260

Merged
oldsj merged 2 commits into
mainfrom
mainloop/fix-queued-message-indicator
Oct 18, 2026
Merged

oldsj merged 2 commits into
mainfrom
mainloop/fix-queued-message-indicator

Conversation

@oldsj-mainloop

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #258. That PR's own report claimed it added a small "queued" badge/state on the message, but its diff never touched a message bubble at all — it only stopped the composer from locking while a turn is in flight (Composer.tsx, ThreadBody.tsx). A message sent while a turn is open — which the server durably queues and 202s as status: "queued" — rendered in the transcript exactly like an already-delivered message, because deliveryNotices() only ever produced a notice for a failed or uncertain delivery row, never for one the server already reports as queued.

The server already carries everything needed to distinguish this: NativeDeliveryInfo.state includes queued in the delivery ledger (api/openapi.yaml), and GET /main-thread / GET /sessions/{id}/native already return that row live (confirmed by reading the server code — this is not a client-only guess). No server change was needed.

  • web/src/lib/delivery.ts: deliveryNotices() now also emits a notice for a queued delivery (label Queued, not retryable — nothing has been attempted yet).
  • web/src/features/chat/DeliveryNotice.tsx: a distinct muted style and explanatory text for the queued notice, separate from the failed (red) and unconfirmed (yellow) ones.
  • web/src/features/chat/MessageBubble.tsx: the bubble itself is dimmed while queued (not just the notice below it), so it reads as pending at a glance; the dimming and notice both clear automatically once the server reports the delivery promoted off queued — no client-side timer.
  • Composer stays non-blocking during a turn, unchanged from Stop the chat composer locking while a turn is in flight #258.
  • Updated docs/specs/chat.md and the message/delivery-receipt design docs to describe the new per-message Queued state (distinct from the thread-level status words, which are unaffected).

Test plan

  • web/src/lib/delivery.test.ts: new unit tests for the queued notice and its clearing once promoted.
  • web/src/features/chat/MessageBubble.test.tsx: queued bubble renders dimmed with a Queued notice and no Retry; reverts to normal once the notice is gone.
  • web/src/features/chat/ChatPage.test.tsx: end-to-end repro — a message sent while a turn is in flight shows Queued and a dimmed bubble, and both clear once the backend promotes the delivery row. Verified this test (and the two unit ones above) fail against the pre-fix code, reproducing the reported bug, and pass after the fix.
  • Full suite: tsc -b, eslint ., vitest run all clean (one unrelated flaky test in hitl-card.test.tsx failed once in the full run and passed in isolation — not touched by this change).
  • Synced to mainloop-dev at this head: healthy, migrations ok, smoke passed.

🤖 Generated with Claude Code

oldsj and others added 2 commits October 18, 2026 18:26
PR #258's own report claimed it added a "queued" badge, but the diff
never touched a message bubble: it only stopped the composer from
locking. A message sent while a turn is in flight renders exactly like
an already-delivered one, because `deliveryNotices()` only ever
produced a notice for a `failed` or `uncertain` delivery, never for
one the server already reports as `queued`.

Surface the existing `queued` delivery-ledger state the same way a
failed or unconfirmed one already is: a distinct `Queued` notice (no
Retry offered, since nothing has been attempted) and a dimmed bubble,
both driven by the same delivery row `GET /main-thread` and
`GET /sessions/{id}/native` already return. The indicator clears on
its own once that row is promoted off `queued` — no client-side timer
or guess.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
web/design/build.ts output for the component docs this PR changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@oldsj
oldsj force-pushed the mainloop/fix-queued-message-indicator branch from ad7dabd to d295645 Compare October 18, 2026 22:29
@oldsj
oldsj merged commit ab8a364 into main Oct 18, 2026
6 checks passed
@oldsj
oldsj deleted the mainloop/fix-queued-message-indicator branch October 18, 2026 22:36
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.

1 participant