Skip to content

fix(sensing): finish #611 NaN-panic audit — 7 sites missed by #613#624

Open
therahul-yo wants to merge 1 commit into
ruvnet:mainfrom
therahul-yo:fix/issue-611-followup-complete-nan-audit
Open

fix(sensing): finish #611 NaN-panic audit — 7 sites missed by #613#624
therahul-yo wants to merge 1 commit into
ruvnet:mainfrom
therahul-yo:fix/issue-611-followup-complete-nan-audit

Conversation

@therahul-yo
Copy link
Copy Markdown
Contributor

Summary

Follow-up to #613 (which closed #611). #613 fixed adaptive_classifier.rs:94 and explicitly stated the audit was complete. That audit grepped for the literal pattern partial_cmp(b).unwrap() and missed seven additional production sites that use comparator variants — all share the same crash class as #611 (NaN in f64 data → partial_cmp returns Noneunwrap() panics the sensing server).

Sites fixed

File Line Function Why it matters
v2/.../adaptive_classifier.rs 205 AdaptiveModel::classify() argmax over softmax probs Same per-frame hot path as #611. A NaN in raw_features survives normalise (NaN / x = NaN) → logits → softmax (exp(NaN)/exp(NaN) = NaN), and the #613 IQR fix does not gate this branch.
v2/.../adaptive_classifier.rs 480, 500 train_from_recordings() argmax Training accuracy / per-class accuracy loops.
v2/.../main.rs 2446, 2449 count_persons_mincut variance source/sink select The outer .unwrap_or((0, &0)) only catches an empty iterator — it cannot rescue a panic raised inside the comparator. A single NaN in variances[] aborts the process.
v2/.../csi.rs 602, 605 same logic, duplicated in csi.rs Same as above.

Fix

Drop-in: swap .unwrap() for .unwrap_or(std::cmp::Ordering::Equal) inside the comparator closure. This is the same one-line behavioural change as #613 and matches the pattern the same files already use elsewhere (e.g. adaptive_classifier.rs:149-150, 155, and now :94 from #613).

No API change, no test changes needed — the existing fix(sensing) commit precedent in CHANGELOG describes this as a behavioural fix only.

Re-audit of remaining partial_cmp(...).unwrap() sites

After this PR, the only remaining partial_cmp(…).unwrap() matches in v2/ are inside #[cfg(test)] / #[test] blocks where inputs are controlled and panic-on-NaN is acceptable:

  • wifi-densepose-signal/src/spectrogram.rs:269#[test] fn test_single_frequency_peak
  • wifi-densepose-pointcloud/src/depth.rs:234 — backproject test
  • ruv-neural/ruv-neural-signal/src/connectivity.rs:477 — coherence test
  • wifi-densepose-sensing-server/src/vital_signs.rs:737#[test] fn test_fft_peak

(#613's PR body listed the first three explicitly as benign; this PR confirms vital_signs.rs:737 is the same category.)

Test plan

  • cd v2 && cargo check -p wifi-densepose-sensing-server --no-default-features (no Rust toolchain available in my environment — relying on CI).
  • cd v2 && cargo test --workspace --no-default-features — 1,031+ passed expected, behavioural change should not affect any test that doesn't already inject NaN.
  • Spot-grep partial_cmp(...).unwrap() after merge — only test/bench sites should remain.

Closes the audit gap from #611 / #613. No related open issue — filing this directly since the gap was caught by following the same grep methodology the original audit described.

…uvnet#613

ruvnet#613 fixed adaptive_classifier.rs:94 (the IQR sort) and called the audit
done, but the grep used `partial_cmp(b).unwrap()` as a literal and missed
seven additional production sites that use comparator variants:

  adaptive_classifier.rs:205  AdaptiveModel::classify() argmax over softmax
                              probs — same per-frame hot path as ruvnet#611.
                              NaN flows through normalise → logits → softmax
                              and still reaches this site even after the
                              IQR fix.
  adaptive_classifier.rs:480  train() argmax (training accuracy loop)
  adaptive_classifier.rs:500  train() per-class argmax
  main.rs:2446, 2449          count_persons_mincut variance source/sink select
  csi.rs:602, 605             count_persons_mincut variance source/sink select
                              (duplicate of main.rs logic in csi.rs)

For the variance-select sites, note that the *outer* `unwrap_or((0, &0))`
only catches an empty iterator — it cannot rescue a panic raised inside
the comparator. A single NaN in `variances[]` still aborts the process.

Same fix as ruvnet#613: swap `.unwrap()` for `.unwrap_or(std::cmp::Ordering::Equal)`
inside the comparator closure. Pure behavioural change, no API surface.

Re-audit of the remaining `partial_cmp(...).unwrap()` matches in v2/:
they are all inside `#[cfg(test)]` / `#[test]` blocks (spectrogram.rs:269,
depth.rs:234, connectivity.rs:477, vital_signs.rs:737) where inputs are
controlled and panic-on-NaN is acceptable.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

adaptive_classifier.rs:94 — partial_cmp(b).unwrap() panics if NaN values enter the classifier

1 participant