From b6571771f3797f156058771f8b9c4e15e2a352ca Mon Sep 17 00:00:00 2001 From: Virgil Date: Wed, 1 Apr 2026 09:07:26 +0000 Subject: [PATCH] refactor(ai): make metric bucket ordering deterministic Co-Authored-By: Virgil --- ai/metrics.go | 8 ++++++-- ai/metrics_test.go | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/ai/metrics.go b/ai/metrics.go index c9bf330..3e89e9c 100644 --- a/ai/metrics.go +++ b/ai/metrics.go @@ -170,7 +170,8 @@ func Summary(events []Event) map[string]any { } } -// sortedCountPairs returns a slice of key-count pairs sorted by count descending. +// sortedCountPairs returns a slice of key-count pairs sorted by count descending, +// with key ascending as a deterministic tie-breaker. func sortedCountPairs(counts map[string]int) []map[string]any { type entry struct { key string @@ -182,7 +183,10 @@ func sortedCountPairs(counts map[string]int) []map[string]any { } slices.SortFunc(entries, func(a, b entry) int { - return cmp.Compare(b.count, a.count) + if result := cmp.Compare(b.count, a.count); result != 0 { + return result + } + return cmp.Compare(a.key, b.key) }) result := make([]map[string]any, len(entries)) diff --git a/ai/metrics_test.go b/ai/metrics_test.go index 4952f12..feeecf7 100644 --- a/ai/metrics_test.go +++ b/ai/metrics_test.go @@ -309,3 +309,20 @@ func TestSortedCountPairs_Good_Ordering(t *testing.T) { t.Errorf("expected third key 'a', got %v", result[2]["key"]) } } + +func TestSortedCountPairs_Good_TieBreakByKey(t *testing.T) { + m := map[string]int{"beta": 2, "alpha": 2, "gamma": 1} + result := sortedCountPairs(m) + if len(result) != 3 { + t.Fatalf("expected 3 entries, got %d", len(result)) + } + if result[0]["key"] != "alpha" { + t.Errorf("expected tie-break to prefer alpha, got %v", result[0]["key"]) + } + if result[1]["key"] != "beta" { + t.Errorf("expected beta second, got %v", result[1]["key"]) + } + if result[2]["key"] != "gamma" { + t.Errorf("expected gamma last, got %v", result[2]["key"]) + } +}