From 2983d66848fed2a78cc599c6eac5da6439396d0a Mon Sep 17 00:00:00 2001 From: Jobdori Date: Sat, 4 Apr 2026 00:45:48 +0900 Subject: [PATCH] fix(runtime): preserve distinct summary lines --- .../crates/runtime/src/summary_compression.rs | 50 +++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/rust/crates/runtime/src/summary_compression.rs b/rust/crates/runtime/src/summary_compression.rs index 30ae276..0f7dbf9 100644 --- a/rust/crates/runtime/src/summary_compression.rs +++ b/rust/crates/runtime/src/summary_compression.rs @@ -109,13 +109,13 @@ fn normalize_lines(summary: &str, max_line_chars: usize) -> NormalizedSummary { continue; } - let truncated = truncate_line(&normalized, max_line_chars); - let dedupe_key = dedupe_key(&truncated); + let dedupe_key = dedupe_key(&normalized); if !seen.insert(dedupe_key) { removed_duplicate_lines += 1; continue; } + let truncated = truncate_line(&normalized, max_line_chars); lines.push(truncated); } @@ -207,7 +207,17 @@ fn omission_notice(omitted_lines: usize) -> String { } fn collapse_inline_whitespace(line: &str) -> String { - line.split_whitespace().collect::>().join(" ") + let trimmed_start = line.trim_start_matches(char::is_whitespace); + if trimmed_start.is_empty() { + return String::new(); + } + + let indent = &line[..line.len() - trimmed_start.len()]; + let collapsed = trimmed_start + .split_whitespace() + .collect::>() + .join(" "); + format!("{indent}{collapsed}") } fn truncate_line(line: &str, max_chars: usize) -> String { @@ -286,6 +296,40 @@ mod tests { assert!(result.omitted_lines > 0); } + #[test] + fn keeps_distinct_long_lines_that_share_a_truncated_prefix() { + // given + let repeated_prefix = "- Current work: ".to_string() + &"x".repeat(180); + let summary = format!("Conversation summary:\n{repeated_prefix} A\n{repeated_prefix} B"); + + // when + let result = compress_summary( + &summary, + SummaryCompressionBudget { + max_chars: 400, + max_lines: 8, + max_line_chars: 80, + }, + ); + + // then + assert_eq!(result.removed_duplicate_lines, 0); + assert_eq!(result.summary.lines().count(), 3); + } + + #[test] + fn preserves_nested_bullet_indentation() { + // given + let summary = "Conversation summary:\n - user: first item\n - assistant: second item"; + + // when + let result = compress_summary(summary, SummaryCompressionBudget::default()); + + // then + assert!(result.summary.contains("\n - user: first item")); + assert!(result.summary.contains("\n - assistant: second item")); + } + #[test] fn provides_a_default_text_only_helper() { // given