Coding standard for recovery/error paths: make failure and recovery behavior truthful, gated, and user-consistent.

Apply this rule set: 1) Ground messages and state on authoritative runtime outcomes (avoid predicates/flags that can drift from behavior). If the only way to know is to attempt (e.g., reset/clear), attempt and then report based on the result. 2) Choose degradation vs failure by comparing which one is less misleading to the user/UI.

Illustrative pattern (retry gating + bounded empty fallback):

if provider_errored || cancel_token.is_cancelled() {
    surface_error(provider_err);
    return;
}

let visible_content_streamed = last_assistant_text_not_empty || surfaced_thinking_in_turn || streamed_images;
if visible_content_streamed {
    // Avoid duplicate/incoherent resend.
    surface_error(provider_err);
    return;
}

let empty_response = no_tools_called && last_assistant_text.is_empty();
if empty_response {
    if empty_turn_retries < MAX_EMPTY_TURN_RETRIES {
        empty_turn_retries += 1;
        warn!("Empty provider response; retrying {}/{}", empty_turn_retries, MAX_EMPTY_TURN_RETRIES);
        messages_to_add.clear(); // don’t persist empty turn
        continue;
    }
    // After retries, surface a real message.
    let msg = Message::assistant().with_text(EMPTY_TURN_MESSAGE);
    yield AgentEvent::Message(msg.clone());
    messages_to_add.push(msg);
}