Skip to content

refactor(association,viz): bring cluster_tracks and render_track_list under S3776 - #157

Merged
montge merged 2 commits into
developfrom
feature/reduce-cognitive-complexity
Sep 20, 2026
Merged

montge merged 2 commits into
developfrom
feature/reduce-cognitive-complexity

Conversation

@montge

@montge montge commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Why

SonarCloud has two open issues on the project, both rust:S3776 (cognitive complexity 17, limit 15): crates/thresh-association/src/jpda.rs::cluster_tracks and crates/thresh-viz/src/app.rs::render_track_list. The quality gate is still OK, but these are the only two open issues, and openspec/specs/cognitive-complexity and CONTRIBUTING both set 15 as the limit.

What

Same phase-helper decomposition as #78, no suppressions, no threshold changes.

cluster_tracks (17 -> 0). The nested find / union functions move to module level as find_root / union_sets: Sonar charges a nested fn's body to the enclosing function at +1 nesting, which alone was 4 of the 17. The three phases become union_tracks_sharing_detections (11), group_tracks_by_root (1) and collect_cluster_detections (1). Loop bodies are moved verbatim: same iteration order, same union direction (parent[rb] = ra), same HashMap, nothing cloned. This function is union-find over a boolean gating matrix, so no floating-point code is touched; the numerical JPDA functions in the file are unchanged.

render_track_list (17 -> 2). The per-row body becomes render_track_row (7), verbatim apart from taking id and confirmed by value and class as Option<&str>.

The counting model was checked against SonarCloud's own per-line increments for both issues, which it reproduces exactly; Sonar's number for the new code appears when it analyses this PR.

Evidence that behaviour is unchanged

  • Old (git show HEAD:) and new cluster_tracks compared on 300,000 seeded random gating matrices (0-13 tracks and detections, 1-70 % density, extra rows, 278,355 non-square): identical after normalising cluster order. Run twice, independently, by the agent that made the change (200,000) and the one that tried to refute it (300,000). The harness is not in the tree, since a copy of the old function would itself be flagged.
  • The three existing cluster tests only asserted the cluster count. New tests, written and passing before the refactor: the full output pinned on a 6x6 case (transitive chain, pair, a track gating nothing, a detection nobody gates), rows beyond n_tracks ignored, and no tracks.
  • Mutation testing of the new code: 8 of 11 single-line mutants were caught, one is equivalent, and two escaped every test: swapping n_tracks / n_dets at the call site, and grouping by parent[i] instead of the root. Every case was square with a flat forest. test_cluster_tracks_non_square_two_level_forest (3 tracks, 2 detections, forest [0, 0, 1]) now fails on both, and passes on the original function.
  • GUI: nothing exercised the click toggle being moved, so there is a headless egui_kittest test, clicking_track_row_toggles_selection, through a new ThreshVizApp::selected_track() accessor (same pattern as ellipses_shown() and the other test accessors). It passed before the refactor and fails when the toggle is broken.

Checks

  • cargo fmt --all -- --check
  • cargo clippy -p thresh-association -p thresh-viz --all-targets -- -D warnings, and again with -p thresh-viz --features gui, which is the run that actually compiles app.rs
  • cargo test -p thresh-association: 55 passed. cargo test -p thresh-viz --features gui: 41 + 6 + 1 passed.
  • RUSTDOCFLAGS=-Dwarnings cargo doc for both crates.
  • Not run: a whole-workspace build or test. cluster_tracks has no callers outside its own tests.

For you to decide

  • CI never lints or tests the GUI code. cargo clippy --workspace and cargo test --workspace run without --features gui, so app.rs, streaming.rs, theme.rs and all six kittest tests (the five existing ones too) are compiled out; viz-build only builds. The new click test therefore runs only locally. A cargo test -p thresh-viz --features gui step in viz-build, where Linux already installs the GUI packages, would close it. Not added here.
  • The confirmation glyph in the track row is computed and then discarded (let _ = status;). Moved verbatim; deleting it or drawing the icon is a UI decision.
  • The order of the clusters that cluster_tracks returns varies from run to run (HashMap with RandomState), before and after. Contents are deterministic and now pinned. A BTreeMap would make the order reproducible if a caller ever needs that.
  • selected_track() is a small addition to the public API; it is in the CHANGELOG line.
  • No OpenSpec delta: the cognitive-complexity spec lists six functions by name and Fix all 19 SonarCloud cognitive complexity violations #78 fixed nineteen without adding to it. Say if you want these two named there.

OpenSpec Validate is expected to be red until #155 merges.

🤖 Generated with Claude Code

https://claude.ai/code/session_01C53cGKw2kFHBrY6cy4tRnP

… under S3776

SonarCloud rust:S3776 reported cognitive complexity 17 (limit 15) for
jpda.rs::cluster_tracks and thresh-viz app.rs::render_track_list, the
project's only two open Sonar issues.

- cluster_tracks: hoist the nested find/union fns to module level (Sonar
  charges nested fns to the enclosing function at +1 nesting) and split
  the union / group-by-root / collect-detections phases into helpers.
  Loop bodies are moved verbatim: same iteration order, same union
  direction, no new allocation. cluster_tracks is now 0; the largest
  helper is 11. The function is union-find over a boolean gating matrix:
  no floating-point code is touched.
- render_track_list: extract render_track_row for the per-row body
  (2 and 7).
- Tests: pin the full output of cluster_tracks on a fixed case (added
  and passing before the refactor); a non-square case with a two-level
  forest, which is the only test that fails when the two counts are
  swapped or when grouping skips the root lookup; focused tests per
  helper; and a headless egui_kittest test for the row click-to-select
  toggle through a new ThreshVizApp::selected_track() accessor.

No behaviour change: old and new cluster_tracks agree on 300,000 seeded
random gating matrices (278,355 non-square).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C53cGKw2kFHBrY6cy4tRnP
Copilot AI lite review requested due to automatic review settings September 19, 2026 14:34

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 59 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 31589f8d-5f01-43c7-903e-688d3960a118

📥 Commits

Reviewing files that changed from the base of the PR and between eb91f22 and 4087cb4.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • crates/thresh-association/src/jpda.rs
  • crates/thresh-viz/src/app.rs
  • crates/thresh-viz/tests/app_kittest.rs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Sep 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@sonarqubecloud

Copy link
Copy Markdown

@montge
montge merged commit 1a399c3 into develop Sep 20, 2026
28 checks passed
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.

2 participants