{"record":{"id":"1d1e78486953450e","repo":"Hmbown/CodeWhale","slug":"compaction-coverage-floor-last-round-tool-call-id-was","errorCode":null,"errorMessage":"Compaction coverage floor: last-round tool call {id} was dropped; history was not replaced.","messagePattern":"Compaction coverage floor: last-round tool call (.+?) was dropped; history was not replaced\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/compaction/last_round.rs","lineNumber":393,"sourceCode":"    }\n    for id in last_round.iter().copied().flat_map(tool_result_ids) {\n        if !replacement\n            .iter()\n            .any(|message| has_tool_result_id(message, &id))\n        {\n            anyhow::bail!(\n                \"Compaction coverage floor: last-round tool result {id} was dropped; history was not replaced.\"\n            );\n        }\n    }\n    // The call, not just its result. Keeping a tool_result whose tool_use was\n    // summarized away leaves an orphaned result that providers reject outright.\n    for id in last_round.iter().copied().flat_map(tool_use_ids) {\n        if !replacement\n            .iter()\n            .any(|message| has_tool_use_id(message, &id))\n        {\n            anyhow::bail!(\n                \"Compaction coverage floor: last-round tool call {id} was dropped; history was not replaced.\"\n            );\n        }\n    }\n    // Match the assistant's actual output. An existential \"some assistant\n    // message survived\" check passed on a replacement whose only assistant\n    // message was the summary the rewrite had just written.\n    for text in last_round.iter().copied().filter_map(assistant_text_of) {\n        if !survives(&text, replacement, assistant_text_of) {\n            anyhow::bail!(\n                \"Compaction coverage floor: last-round assistant output was dropped; history was not replaced.\"\n            );\n        }\n    }\n    if last_round\n        .iter()\n        .any(|message| message.role.is_assistant_like())\n        && !replacement","sourceCodeStart":375,"sourceCodeEnd":411,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/compaction/last_round.rs#L375-L411","documentation":"The survival contract requires every tool call (tool_use) from the last round to survive compaction. Keeping a tool_result while summarizing away its tool_use leaves an orphaned result that providers reject outright, so a replacement dropping a tool_use id is rejected before it can replace history.","triggerScenarios":"`validate_last_round_coverage` iterates last-round `tool_use_ids` and finds no message in the replacement carrying that tool_use id.","commonSituations":"A rewrite keeps tool results but summarizes the assistant message containing the tool calls; a partial rewrite drops the assistant tool_use block; provider-strict pairing means this must fail fast.","solutions":["Carry the last-round tool_use blocks (and their assistant message) into the replacement unmodified.","Ensure tool_use and its tool_result are kept or dropped as a pair — never one alone.","Fix summarization boundaries so only pre-last-round turns are rewritten.","Check `tool_use_ids` extraction covers all last-round messages."],"exampleFix":"// before: orphaned result\nreplacement = [summary, tool_result_msg];\n// after: keep call+result pair\nreplacement = [summary, tool_use_msg, tool_result_msg];","handlingStrategy":"validation","validationCode":"fn keeps_tool_uses(last_round: &[Message], replacement: &[Message]) -> bool {\n    last_round.iter().flat_map(tool_use_ids)\n        .all(|id| replacement.iter().any(|m| has_tool_use_id(m, &id)))\n}","typeGuard":null,"tryCatchPattern":"match compaction::apply_replacement(&replacement) {\n    Err(e) if e.to_string().contains(\"tool call\") => keep_original_history(),\n    other => other,\n}","preventionTips":["Never summarize away the assistant message holding last-round tool_use blocks","Keep tool_use/tool_result pairs together in rewrites","Add fixture coverage for tool-bearing last rounds","Run the coverage-floor test suite after compaction changes"],"tags":["compaction","coverage-floor","tool-use"],"backgroundTag":"internal-invariant-violation","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}