Agent pane: never block the composer on a slow send (per-message delivery state)
Summary
Draft. The design is in this PR first: plans/cmux-next/composer-delivery-DESIGN.md. The build follows in later commits.
Leo's ask: never block the composer on slow delivery (remote machines, lag). Each sent message shows at once as its own row with a small delivery indicator. You can keep sending. Send never turns into a spinner.
- Per-message states: sending → delivered (✓) → read (✓✓, icon owned by the message-actions lane) or failed.
- A failed message gets Retry, Edit and Remove, plus Details: which hop failed (app → acpmux → host → agent), the error, the latency of each hop, timestamps, and Copy debug info.
- Timeout: "Still sending" after 10 s; failed after 60 s or when the connection closes.
- Duplicates: the client prompt id is the delivery key. acpmux runs a prompt id once (
hub/turns.rs prompt_with,_meta.acpmux.resend), so a resend after a reconnect, or a Retry, never starts a second turn. - Also folds in the two edge cases from the #22732 review: Trust re-sends that row's prompt, not the composer's draft, and a refused first prompt keeps its workspace context.
Testing
Design only so far; tests are listed in the design note.
Observed / Not run
- Not run: nothing built yet.
Changelog
none (draft)
Agent session: cosmic-gray
🤖 Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Implements per-message delivery state in the agent pane so the composer never blocks on a slow send. Sends in a started chat leave the composer at once and appear as their own row with a delivery marker; only a new chat's first prompt still holds in the composer.
- Rows move sending → delivered → read, or failed, with Retry, Edit, Remove, and Details (hop, error, latency, timestamps, Copy debug info).
- The read check is the bubble's single check, owned by the message-actions lane and driven by delivery state; a steer still reads Unread while it waits for the agent, whatever its delivery says. The delivery mark is only a clock (and "Still sending") beside a prompt still on its way, absolutely placed so it never shifts the rows below.
- The client prompt id is the delivery key; Retry and reconnects resend the same id with
resend, so acpmux never runs a second turn, and a late acceptance still lands. A Retry behind a running turn waits in the queue. - A send with no acceptance shows "Still sending" after 10 s and fails after 60 s; a sender that still holds its prompt gets no timed-out row, and a late acceptance frees the retry.
- A prompt whose connection dropped before acpmux took it goes back to the composer, not to a failed row; a prompt a harness switch hands back is not restored twice.
- Trust re-sends the failed row with its send's gesture, never the composer's draft, and workspace context is spent only on real acceptance.
Testing
- Adds gallery coverage for the delivery marks and failed-prompt actions.