Observed behavior
Captured from the deferred t.Logf observation in TestCloudClientSearchRRFArithmetic/Max_0 (Phase 21.1 Pass 1 run against Chroma Cloud):
pass1 degenerate Max_0: err=<nil> IDs=[[1 2 3 4 5]] Scores=[[0 0 0 0 0]]
The server returned searchErr=nil, an outer IDs slice with exactly one inner entry [1 2 3 4 5] in default insertion order, and an outer Scores slice with one inner entry where every score is exactly 0.0.
Expected behavior
EITHER:
- Client-side guard (preferred):
RrfRank.Max(FloatOperand(0.0)) should reject at construction time (or return an ErrorRank) because max(x, 0) is provably meaningless on RRF's non-positive fusion output. The client has full visibility into this composition and can detect it statically.
- Server-side signaling: If the client accepts the composition, the server should signal "degenerate result" via a response header (
X-Chroma-Degenerate: all_tied) or a structured warning so the Go client can propagate it to the caller instead of returning an all-tied result set that looks like a valid ranking.
Currently the client accepts rrf.Max(FloatOperand(0.0)) without complaint and the server returns an all-tied result set with insertion-order IDs — this looks like a normal search response from the caller's perspective but carries no ranking information.
Wire-level explanation
From .planning/phases/21.1-rrf-cloud-integration-test-coverage-including-arithmetic-com/21.1-RESEARCH.md § Pitfall 4:
RrfRank.MarshalJSON() in pkg/api/v2/rank.go:1217 auto-negates the fusion score before serializing:
// Negate (RRF gives higher scores for better, Chroma needs lower for better)
result := rrfSum.Negate()
return result.MarshalJSON()
So rrf.Max(FloatOperand(0.0)) on the wire evaluates max(-rrf_sum, 0). Because -rrf_sum <= 0 for every doc when the raw RRF sum is non-negative (which is always true for RRF — sum(1/(k+rank)) >= 0), max(-rrf_sum, 0) = 0 for every doc. Every score collapses to 0.0, producing an all-tied result set with no ranking information.
Classification
Client-API-contract defect per the L1 rubric in .planning/phases/21.1-rrf-cloud-integration-test-coverage-including-arithmetic-com/21.1-REVIEWS.md § Action Item L1. The client could reject rrf.Max(FloatOperand(0.0)) at construction time as a mathematically meaningless composition on RRF's non-positive fusion output. Alternatively (or additionally), the server could signal the degenerate state via a response header.
This is tagged [ENH] (not [BUG]) because:
- The server is mathematically correct —
max(x, 0) = 0 for x <= 0 is the definition.
- The Go client accepted a composition that it had enough information to reject at construction.
- The remediation is "add a client-side guard" or "add a server-side warning channel", both of which are enhancements to the existing contract, not fixes to broken behavior.
Surfaced by
pkg/api/v2/client_cloud_test.go :: TestCloudClientSearchRRFArithmetic/Max_0 (added in Phase 21.1 Pass 1; pinned as a regression assertion in Pass 2).
Repro
export CHROMA_API_KEY=<your key>
export CHROMA_DATABASE=<your database>
export CHROMA_TENANT=<your tenant>
go test -tags="basicv2 cloud" -v -run "TestCloudClientSearchRRFArithmetic/Max_0" ./pkg/api/v2/...
The test PASSES today because Pass 2 pins the observed all-zero collapse as a regression assertion (per-score require.Equal(float64(0), s, ...) loop and require.Equal([]DocumentID{"1","2","3","4","5"}, sr.IDs[0])). When the client-side guard lands, the pin will flip as part of the enhancement phase.
Scope
Phase 21.1 pins this behavior as a regression assertion. Client-side guards and server-side signaling are deferred per D-20 (Phase 21.1 does not fold fixes into its own scope — each discovered contract gap becomes its own phase).
Observed behavior
Captured from the deferred
t.Logfobservation inTestCloudClientSearchRRFArithmetic/Max_0(Phase 21.1 Pass 1 run against Chroma Cloud):The server returned
searchErr=nil, an outerIDsslice with exactly one inner entry[1 2 3 4 5]in default insertion order, and an outerScoresslice with one inner entry where every score is exactly0.0.Expected behavior
EITHER:
RrfRank.Max(FloatOperand(0.0))should reject at construction time (or return anErrorRank) becausemax(x, 0)is provably meaningless on RRF's non-positive fusion output. The client has full visibility into this composition and can detect it statically.X-Chroma-Degenerate: all_tied) or a structured warning so the Go client can propagate it to the caller instead of returning an all-tied result set that looks like a valid ranking.Currently the client accepts
rrf.Max(FloatOperand(0.0))without complaint and the server returns an all-tied result set with insertion-order IDs — this looks like a normal search response from the caller's perspective but carries no ranking information.Wire-level explanation
From
.planning/phases/21.1-rrf-cloud-integration-test-coverage-including-arithmetic-com/21.1-RESEARCH.md § Pitfall 4:RrfRank.MarshalJSON()inpkg/api/v2/rank.go:1217auto-negates the fusion score before serializing:So
rrf.Max(FloatOperand(0.0))on the wire evaluatesmax(-rrf_sum, 0). Because-rrf_sum <= 0for every doc when the raw RRF sum is non-negative (which is always true for RRF —sum(1/(k+rank)) >= 0),max(-rrf_sum, 0) = 0for every doc. Every score collapses to0.0, producing an all-tied result set with no ranking information.Classification
Client-API-contract defect per the L1 rubric in
.planning/phases/21.1-rrf-cloud-integration-test-coverage-including-arithmetic-com/21.1-REVIEWS.md § Action Item L1. The client could rejectrrf.Max(FloatOperand(0.0))at construction time as a mathematically meaningless composition on RRF's non-positive fusion output. Alternatively (or additionally), the server could signal the degenerate state via a response header.This is tagged
[ENH](not[BUG]) because:max(x, 0) = 0forx <= 0is the definition.Surfaced by
pkg/api/v2/client_cloud_test.go :: TestCloudClientSearchRRFArithmetic/Max_0(added in Phase 21.1 Pass 1; pinned as a regression assertion in Pass 2).Repro
The test PASSES today because Pass 2 pins the observed all-zero collapse as a regression assertion (per-score
require.Equal(float64(0), s, ...)loop andrequire.Equal([]DocumentID{"1","2","3","4","5"}, sr.IDs[0])). When the client-side guard lands, the pin will flip as part of the enhancement phase.Scope
Phase 21.1 pins this behavior as a regression assertion. Client-side guards and server-side signaling are deferred per D-20 (Phase 21.1 does not fold fixes into its own scope — each discovered contract gap becomes its own phase).