diff --git a/codex-rs/file-search/src/lib.rs b/codex-rs/file-search/src/lib.rs index 8754181670..0b0c3949ec 100644 --- a/codex-rs/file-search/src/lib.rs +++ b/codex-rs/file-search/src/lib.rs @@ -183,7 +183,13 @@ pub async fn run( } let mut matches: Vec<(u32, String)> = global_heap.into_iter().map(|r| r.0).collect(); - matches.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal)); + // Sort by descending score, then ascending path for deterministic ordering. + matches.sort_by(|a, b| { + match b.0.cmp(&a.0) { + std::cmp::Ordering::Equal => a.1.cmp(&b.1), + other => other, + } + }); Ok(FileSearchResults { matches, @@ -281,4 +287,28 @@ mod tests { let score = pattern.score(haystack, &mut matcher); assert_eq!(score, None); } + + #[test] + fn tie_breakers_sort_by_path_when_scores_equal() { + let mut matches = vec![ + (100, "b_path".to_string()), + (100, "a_path".to_string()), + (90, "zzz".to_string()), + ]; + + // Sort using the same comparator as production code. + matches.sort_by(|a, b| match b.0.cmp(&a.0) { + std::cmp::Ordering::Equal => a.1.cmp(&b.1), + other => other, + }); + + // Highest score first; ties broken alphabetically. + let expected = vec![ + (100, "a_path".to_string()), + (100, "b_path".to_string()), + (90, "zzz".to_string()), + ]; + + assert_eq!(matches, expected); + } }