Skip to content

[ENH] Reject mathematically meaningless RrfRank arithmetic compositions at build time #501

@tazarov

Description

@tazarov

Summary

RrfRank.MarshalJSON auto-negates its fusion score, so the expression tree always operates on -rrf_sum, which is always ≤ 0 on any realistic corpus. This makes several arithmetic compositions mathematically meaningless or degenerate:

Composition Reason
rrf.Log() Log of non-positive is undefined → server silently drops scores
rrf.Max(Val(0)) max(x, 0) == 0 for x ≤ 0 → all scores collapse to 0
rrf.Min(Val(0)) min(x, 0) == x for x ≤ 0 → no-op (harmless but pointless)

The degenerate runtime behavior is tracked separately as a [BUG]. This enhancement is the complementary client-side preventive measure: reject these compositions at construction time so the caller sees a clear error before a query is ever sent.

Proposed Design

Add a lightweight Validate() method (or fail-fast in the arithmetic methods themselves) on the wrapper ranks that detect an inner *RrfRank and reject the degenerate compositions.

Example sketch:

func (r *RrfRank) Log() Rank {
    return &deferredErrorRank{
        err: errors.New("RrfRank.Log(): log of the auto-negated fused score is undefined — " +
            "RRF fusion output is always non-positive after internal negation"),
    }
}

The error surfaces at MarshalJSON time (same pattern as UnknownRank at rank.go:85), keeping the fluent API ergonomic while preventing silent footguns.

Alternatives

  1. Hard error return from the arithmetic methods — breaks the fluent API (Rank return type would need to become (Rank, error)).
  2. Compile-time rejection via separate types — e.g., split RrfRank into a narrower type that does not implement Log/Max(0). Overkill.
  3. Runtime warning via a package-level logger — too easy to miss; not idiomatic for a library.

The deferred-error pattern (option shown above) is the best fit for this codebase.

Scope Note

This enhancement only addresses the client-side preventive guard. The underlying server-side silent degeneration is tracked in the accompanying [BUG] issue.

Discovery

Flagged during PR review of #496 by the silent-failure-hunter agent and pinned as a regression baseline in Phase 21.1 cloud tests (TestCloudClientSearchRRFArithmetic).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions