Optimistic Retries Preserve User Intent

Optimistic retries preserve user intent

If an optimistic UI has already told the user "done", a transient failure should not immediately roll the world back. Rollback means "we know this did not apply", not "the database pool was busy for two seconds".

The user has already moved on. If they archived a message and the row disappeared, bringing it back a minute later is not a neutral correction; it breaks the contract the UI just made.

The rule

Retry transient optimistic mutations before reconciliation.

Keep the optimistic state while retrying. Keep the rollback snapshot too. Only reconcile after bounded retries are exhausted or after the error is clearly terminal: validation failure, permission failure, missing account, unsupported provider capability, and so on.

Mxr example

In Mxr, the TUI queues optimistic mailbox mutations through QueuedMutation. The current code truth:

That last distinction matters. Auto-mark-read is background politeness. Archive, trash, spam, label, and explicit read/unread are user intent. Both can retry; only explicit intent deserves an error surface after the retries fail.

What this generalizes to

Use this when:

Do not use it when:

The pattern is not "hide errors". It is "do not confuse temporary admission failure with semantic failure".

See also