Hmbown/CodeWhale · error
Compaction coverage floor: last-round assistant output was…
Error message
Compaction coverage floor: last-round assistant output was dropped; history was not replaced.
What it means
The survival contract matches the assistant's actual output text from the last round. An existential 'some assistant message survived' check was insufficient — a replacement whose only assistant message was the summary itself passed it. This check requires each last-round assistant text to appear in the replacement; otherwise the rewrite is rejected.
Solutions
- Keep the last-round assistant output text verbatim in the replacement history.
- Fix the rewrite so the summary does not displace the actual last assistant answer.
- Ensure `assistant_text_of` covers all text blocks so validation sees the real output.
- Run `coverage_floor_rejects_assistant_output_replaced_by_a_summary` to reproduce.
Example fix
// before: summary replaced the assistant answer replacement = [summary_msg]; // after replacement = [summary_msg, original_assistant_msg];
Defensive patterns
Strategy: validation
Validate before calling
fn keeps_assistant_text(last_round: &[Message], replacement: &[Message]) -> bool {
last_round.iter().filter_map(assistant_text_of)
.all(|t| replacement.iter().any(|m| assistant_text_of(m) == Some(t)))
} Try / catch
match compaction::apply_replacement(&replacement) {
Err(e) if e.to_string().contains("assistant output was dropped") => keep_original_history(),
other => other,
} Prevention
- Do not replace the last assistant answer with the summary
- Check 'some assistant message survived' is not enough — the summary itself counts
- Keep real assistant output verbatim in the replacement
- Cover this with the assistant-replaced-by-summary test
When it happens
Trigger: `validate_last_round_coverage` finds a last-round assistant text block whose content is not present in any message of the replacement history (per `survives`/`assistant_text_of`).
Common situations: A rewrite replaces the last assistant answer with the summary; a naive compaction keeps 'an assistant message' but it is the summary, not the real answer; regression in rewrite code after refactor.
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
- Compaction coverage floor: a last-round user message was…
- Compaction coverage floor
- Compaction coverage floor: last-round tool call
- Compaction coverage floor: last-round tool result
- Compaction coverage floor: checkpoint receipt was dropped…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/d622097122dd2cc1.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/compaction/last_round.rs:403
}
// 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
.iter()
.any(|message| has_tool_use_id(message, &id))
{
anyhow::bail!(
"Compaction coverage floor: last-round tool call {id} was dropped; history was not replaced."
);
}
}
// Match the assistant's actual output. An existential "some assistant
// message survived" check passed on a replacement whose only assistant
// message was the summary the rewrite had just written.
for text in last_round.iter().copied().filter_map(assistant_text_of) {
if !survives(&text, replacement, assistant_text_of) {
anyhow::bail!(
"Compaction coverage floor: last-round assistant output was dropped; history was not replaced."
);
}
}
if last_round
.iter()
.any(|message| message.role.is_assistant_like())
&& !replacement
.iter()
.any(|message| message.role.is_assistant_like())
{
anyhow::bail!(
"Compaction coverage floor: last-round assistant output was dropped; history was not replaced."
);
}
Ok(())
}
View on GitHub (pinned to 73e0f67d83)