Sweep: 91 panicky float sorts (`sort_by(partial_cmp().unwrap())`) across 27 crates — NaN-reachable in core query paths (ADR-340 invariant 1)
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 4.5k
- Forks
- 603
- Avg merge
- 23h 32m
- Merged PRs (30d)
- 59
Description
Defect class
The #901 fix (NaN candidate confidence panicking route()) closed one instance of a pattern the workspace has 91 occurrences of, across 27 crates:
xs.sort_by(|a, b| a.partial_cmp(b).unwrap())
partial_cmp returns None when either operand is NaN, so every one of these sites panics the moment a NaN reaches it with 2+ elements. Per ADR-340 invariant 1, sorts over floats that originate outside the module's control must reject non-finite values at the boundary or use total_cmp (preferably both).
Inventory
grep -rn "sort_by(.*partial_cmp.*unwrap())" crates/*/src (excluding unwrap_or variants): 91 hits. Affected crates:
emergent-time, mcp-brain-server, ruvector-bench, ruvector-bounded-rag, ruvector-capgated, ruvector-consciousness, ruvector-core, ruvector-diskann, ruvector-entropy-ann, ruvector-graph, ruvector-graph-transformer, ruvector-hyperbolic-hnsw, ruvector-lsm-ann, ruvector-math, ruvector-matryoshka, ruvector-maxsim, ruvector-mincut-gated-transformer, ruvector-postgres, ruvector-query-cache, ruvector-sota-bench, ruvector-sparse-inference, ruvector-streaming-qng, ruvector-temporal-coherence, ruvector-tiny-dancer-core (non-router files), ruvllm, ruvllm-cli, ruvllm-wasm
Triage order — user-reachable first
Not all 91 are equal. The ones in query paths fed by user-supplied vectors are remotely triggerable: inserting or querying with a NaN component makes every distance NaN, and the first result sort panics the server. Highest priority:
ruvector-core/src/advanced_features/hybrid_search.rs:248— result score sortruvector-core/src/advanced_features/diskann.rs:182,186,202,293,297,352— distance sorts in searchruvector-core/src/advanced_features/product_quantization.rs:257— distance sortruvector-core/src/advanced_features/conformal_prediction.rs:148,316— score quantilesruvector-core/src/agenticdb.rs:664— utility-score sortruvector-core/src/advanced/{neural_hash.rs:297, tda.rs:93,301, hypergraph.rs:262,446}ruvector-graph/src/typed_graph.rs:745
ruvector-postgres deserves special mention: a panic inside a Postgres extension aborts the backend process, so a single NaN in a query vector is a crash for every connection sharing it.
Second tier: bench/CLI/analysis crates (ruvector-bench, ruvllm-cli, mcp-brain-server, …) where a panic costs a run, not a service.
Suggested mechanics
- Mechanical rewrite
a.partial_cmp(b).unwrap()→a.total_cmp(b)where the element type isf32/f64directly, andf32::total_cmpon the projected key for tuple/struct sorts. This is behavior-preserving for all finite inputs and NaN-total otherwise. - Where the crate already has an input-validation boundary (e.g.
ruvector-coreinsert/query dimension checks), also reject non-finite components there so degenerate inputs fail loudly instead of ordering NaNs — matching the #901 fix shape. - Add one shared regression pattern: query with a NaN component must return an error (validated boundary) or a stable ordering (total_cmp), never a panic.
- A clippy lint can hold the line:
disallowed_methodsforpartial_cmpinsidesort_byisn't directly expressible, but a repo-levelgrepgate in CI (like the ADR index check) is trivial and catches regressions.
Found during the ADR-340 hardening pass; #901 fixed the router instance, this issue tracks the remaining 91.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the inventory command and the priority sites in ruvector-core/src/advanced_features/, ruvector-core/src/agenticdb.rs, and ruvector-graph/src/typed_graph.rs. Check the existing #901 validation shape and identify relevant insert/query tests before working through the 91 hits. Done means no listed panicking float sorts remain, user-reachable NaN inputs error or sort stably, and the regression pattern and CI grep gate are covered.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend, databases, security, testing
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 28/100