{"record":{"id":"29ed6597c4edfa23","repo":"Hmbown/CodeWhale","slug":"compaction-coverage-floor-last-round-tool-result-id-was","errorCode":null,"errorMessage":"Compaction coverage floor: last-round tool result {id} was dropped; history was not replaced.","messagePattern":"Compaction coverage floor: last-round tool result (.+?) was dropped; history was not replaced\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/compaction/last_round.rs","lineNumber":381,"sourceCode":"    }\n    // Every user turn in the round, not the first one `find_map` happens to\n    // reach. `last_round_start` walks back past a toolless tail to the previous\n    // tool-bearing turn, so the round routinely spans two user messages -- and\n    // checking only the earliest let a rewrite drop the *latest* one, which is\n    // the turn this whole contract exists to keep.\n    for text in last_round.iter().copied().filter_map(user_prompt_text_of) {\n        if !survives(&text, replacement, user_prompt_text_of) {\n            anyhow::bail!(\n                \"Compaction coverage floor: a last-round user message was dropped; history was not replaced.\"\n            );\n        }\n    }\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","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/compaction/last_round.rs#L363-L399","documentation":"The survival contract requires every tool result from the last round to survive compaction. If a replacement history drops a tool result with a given id, the rewrite is rejected. Dropping tool results breaks the tool_use/tool_result pairing providers require.","triggerScenarios":"`validate_last_round_coverage` iterates last-round `tool_result_ids` and finds no message in the replacement containing that tool result id.","commonSituations":"A rewrite summarizes the last round's tool exchange into prose; a filtering bug drops tool-result blocks; newly added compaction logic prunes 'large' tool outputs including last-round ones.","solutions":["Preserve the last-round tool results verbatim in the replacement history.","Fix the rewrite so summarization only applies to rounds before the last one.","Verify `tool_result_ids` extraction covers all messages of the last round (a missed message hides ids from validation).","Run the coverage-floor tests to identify the exact drop."],"exampleFix":"// before: tool results summarized away\nlet replacement: Vec<Message> = summarize(last_round);\n// after: copy tool-result blocks through untouched\nlet replacement: Vec<Message> = keep_tool_blocks(last_round);","handlingStrategy":"validation","validationCode":"fn keeps_tool_results(last_round: &[Message], replacement: &[Message]) -> bool {\n    last_round.iter().flat_map(tool_result_ids)\n        .all(|id| replacement.iter().any(|m| has_tool_result_id(m, &id)))\n}","typeGuard":null,"tryCatchPattern":"match compaction::apply_replacement(&replacement) {\n    Err(e) if e.to_string().contains(\"tool result\") => keep_original_history(),\n    other => other,\n}","preventionTips":["Copy tool_result blocks through verbatim for the last round","Summarize tool output only in older rounds","Test rewrites against multi-tool rounds","Remember providers reject orphaned results — pair with their tool_use"],"tags":["compaction","coverage-floor","tool-result"],"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"}