Agent pane: a failed turn's Retry says Retrying… and never settles as sent when nothing was sent

AU
3d ago
+202-30

Fixes https://github.com/manaflow-ai/cmux/issues/34369

Why Retry looked dead

The failed turn's card Retry (TurnRows.tsx → errorRecovery.ts → App.tsx retryFailed → acpmux _acpmux/retry) had two ways to show nothing:

  1. Paths that sent nothing settled as sent. retryFailed returned undefined (the "it went" value) when the pane had no live acpmux client (the daemon went away and the client isn't back yet, while the last snapshot still reads connected) or the pane had moved to another chat. resendToOwner did the same when the chat or client changed. The card took that as success: no note, and the button came straight back.
  2. An accepted retry left the card looking untouched. Once acpmux took the retry, the button re-enabled immediately with nothing on the card. During a gateway outage the retried attempt fails again within seconds and draws the same error card, so the click read as doing nothing.

Change

  • App.tsx retryFailed: no client, or a chat the pane no longer reaches, settles retry.failed ("Couldn't retry").
  • turnRetry.ts resendToOwner: rejects with RetryNotSent when it sends nothing, so retryFailedTurn reports retry.failed.
  • TurnRows.tsx: the card's note says Retrying… (Continuing… for Continue) from the click until the retried attempt reaches the chat and changes the row. The button is disabled only while the request is in flight, so a row that never changes can't leave it stuck. A second click goes to acpmux, which refuses it with "Busy, try again shortly" while the attempt runs.
  • Two new strings, turn.retrying and turn.continuing, in all 21 locales (Retrying… reuses host.retryQueued's translations).

acpmux's side (retry_turn.rs) is unchanged: it already re-runs the failed turn, or refuses with a reason the card shows.

Tests (focused, local bun)

  • test/turn-retry.test.tsx (11 pass): new tests for "Retrying… until its attempt reaches the chat" and "a fallback send that reaches no chat says Retry could not run"; the ownership test now asserts the not-sent rejection.
  • Also run, no failures: turn-error-continue, turn-error-card, access-offer, cloud-signin-card, TurnFooter, footerActions, ContinueAfterStop, transcript-turn-retry, strings-build-output; check-xcstrings-keys.py; vp fmt --check on the changed files.

Dogfood build and review receipts follow in comments.

🤖 Generated with Claude Code