diff --git a/headroom/transforms/adaptive_sizer.py b/headroom/transforms/adaptive_sizer.py index 9a867d60a..5f73aabd8 100644 --- a/headroom/transforms/adaptive_sizer.py +++ b/headroom/transforms/adaptive_sizer.py @@ -51,7 +51,12 @@ def compute_optimal_k( # Tier 1: Fast path if n <= 8: - return n + # Still honor the caller's hard cap: ``max_k`` is documented as "never + # return more than this". Returning the raw ``n`` let a small ``max_k`` + # (e.g. a tight search/log budget) be exceeded on tiny inputs, so the + # caller kept more items than it asked for. The near-duplicate branch + # below already clamps to ``effective_max``; do the same here. + return min(n, effective_max) # Check for near-total redundancy unique_count = count_unique_simhash(items) diff --git a/tests/test_adaptive_sizer.py b/tests/test_adaptive_sizer.py index f20746d56..f9ae55c6d 100644 --- a/tests/test_adaptive_sizer.py +++ b/tests/test_adaptive_sizer.py @@ -107,6 +107,18 @@ class TestSmallArrays: items = _make_unique_items(8) assert compute_optimal_k(items) == 8 + def test_small_array_respects_max_k(self): + """A small array (n <= 8) must still honor a tight ``max_k`` cap. + + ``max_k`` is documented as "never return more than this"; the fast path + used to return the raw ``n`` and blow past a small cap. + """ + items = _make_unique_items(8) + assert compute_optimal_k(items, max_k=5) == 5 + assert compute_optimal_k(items, max_k=3) == 3 + # A cap >= n leaves the array unchanged. + assert compute_optimal_k(items, max_k=20) == 8 + class TestNearTotalRedundancy: def test_identical_items_returns_min(self):