Hmbown/CodeWhale · error

Compaction coverage floor: a last-round user message was…

Error message

Compaction coverage floor: a last-round user message was dropped; history was not replaced.

What it means

The last-round survival contract validates that a compaction replacement history keeps every message of the most recent conversation round. This fires when any user prompt text from the last round is absent from the replacement, so the rewrite is rejected and history is not replaced. It exists because the round can span two user messages and dropping the latest one silently loses the user's most recent request.

Solutions

  1. Fix the compaction rewrite so every last-round user message is carried into the replacement history verbatim.
  2. Re-check the `last_round_start` computation — it must walk past a toolless tail to the previous tool-bearing turn and include ALL user messages in the round, not just the earliest.
  3. Run the coverage-floor tests (`coverage_floor_rejects_*`) to see which scenario reproduces.
  4. Ensure summaries are prepended/summarized only for turns strictly before the last round.

Example fix

// before: replacement only summarizes, dropping the latest user turn
let replacement = vec![summary_message];
// after: keep the whole last round
let replacement = [summary_message, last_round.as_slice()].concat();
Defensive patterns

Strategy: validation

Validate before calling

fn keeps_last_round_users(last_round: &[Message], replacement: &[Message]) -> bool {
    last_round.iter().filter_map(user_prompt_text_of)
        .all(|t| replacement.iter().any(|m| user_prompt_text_of(m) == Some(t)))
}

Try / catch

if let Err(e) = compaction::apply_replacement(&replacement) {
    if e.to_string().contains("coverage floor") { /* keep original history */ }
}

Prevention

When it happens

Trigger: `validate_last_round_coverage` (via `validate_survival_contract` or coverage-floor tests) finds a last-round user message whose text does not appear in the replacement history.

Common situations: A custom or newly-introduced compaction rewrite drops the latest user turn; an off-by-one in `last_round_start` boundary computation; editing compaction code so older turns are summarized but the current user turn is also swallowed.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/ccd16249fff6c478. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/compaction/last_round.rs:371

    })
}

pub(crate) fn validate_last_round_coverage(
    original: &[Message],
    replacement: &[Message],
) -> Result<()> {
    let last_round = protected_last_round(original);
    if last_round.is_empty() {
        return Ok(());
    }
    // Every user turn in the round, not the first one `find_map` happens to
    // reach. `last_round_start` walks back past a toolless tail to the previous
    // tool-bearing turn, so the round routinely spans two user messages -- and
    // checking only the earliest let a rewrite drop the *latest* one, which is
    // the turn this whole contract exists to keep.
    for text in last_round.iter().copied().filter_map(user_prompt_text_of) {
        if !survives(&text, replacement, user_prompt_text_of) {
            anyhow::bail!(
                "Compaction coverage floor: a last-round user message was dropped; history was not replaced."
            );
        }
    }
    for id in last_round.iter().copied().flat_map(tool_result_ids) {
        if !replacement
            .iter()
            .any(|message| has_tool_result_id(message, &id))
        {
            anyhow::bail!(
                "Compaction coverage floor: last-round tool result {id} was dropped; history was not replaced."
            );
        }
    }
    // The call, not just its result. Keeping a tool_result whose tool_use was
    // summarized away leaves an orphaned result that providers reject outright.
    for id in last_round.iter().copied().flat_map(tool_use_ids) {
        if !replacement

View on GitHub (pinned to 73e0f67d83)