fix(tracker): exclude Lost tracks from bridge output (#420, ADR-082)#426
Merged
fix(tracker): exclude Lost tracks from bridge output (#420, ADR-082)#426
Conversation
`tracker_bridge::tracker_to_person_detections` documented itself as filtering to `is_alive()` but never actually filtered — it forwarded every non-Terminated track to the WebSocket stream. With 3 ESP32-S3 nodes × ~10 Hz CSI, transient detections that fell outside the Mahalanobis gate created a steady stream of new Tentative tracks that aged through Active and into Lost. Lost tracks are kept in the tracker for `reid_window` (~3 s) so re-identification can match them when a similar detection reappears, but they are NOT currently observed and must not render as live skeletons. Up to ~90 ghost skeletons could accumulate at any moment, hence the 22-24 phantoms users saw while `estimated_persons` correctly reported 1. Add `PoseTracker::confirmed_tracks()` that returns only `Tentative ∪ Active` and rewire the bridge to use it. `Lost` tracks remain in the tracker for re-ID; they just no longer ship to the UI. `active_tracks()` is left unchanged for the AETHER re-ID consumers (ADR-024). Regression test `test_lost_tracks_excluded_from_bridge_output` drives a track to Active, lapses for `loss_misses + 1` ticks to push it to Lost, and asserts `tracker_update` returns an empty Vec while the Lost track is still present in `all_tracks()` (re-ID still works). Validated: - cargo test --workspace --no-default-features → 1,539 passed, 0 failed - ESP32-S3 on COM7 still streaming live CSI (cb #32800) Co-Authored-By: claude-flow <ruv@ruv.net>
This was referenced Apr 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #420.
Symptom
3× ESP32-S3 nodes streaming CSI to the Rust sensing server.
GET /api/v1/sensing/latestreportsestimated_persons: 1. The live UI renders 22-24 phantom skeletons that flicker at high rate.Root cause
crates/wifi-densepose-sensing-server/src/tracker_bridge.rs::tracker_to_person_detectionsdocumented itself as:…but called
tracker.active_tracks(), which itself filters byis_alive().is_alive()returns true forTentative ∪ Active ∪ Lost— every non-Terminatedstate. The bridge therefore shipped all non-terminated tracks to the WebSocket stream.Losttracks are deliberately kept in the tracker forreid_window(~3 s by default) so re-identification can match them when a similar detection reappears. They are by definition not currently observed (they've missedloss_missesupdates already). Rendering them as live skeletons is the bug.With 3 nodes × 10 Hz CSI, every Mahalanobis-gate miss creates a new Tentative track and ages the previous one to Lost. Up to
reid_window × n_nodes≈ 90 phantom Lost tracks can co-exist at once. The actually-observed person is one of them; the rest are ghosts.Fix
PoseTracker::confirmed_tracks()— returns onlyTentative ∪ Active. (Tentativeis included so first-detection visibility latency stays at one tick.)tracker_to_person_detectionsto callconfirmed_tracks().active_tracks()is unchanged — AETHER re-ID consumers (ADR-024) need the full alive set.TrackerConfigfromsignal::ruvsenseso consumers can construct configured trackers without reaching intopose_tracker::.Pure egress filter. No state-machine change. No schema change.
ADR
ADR-082 — Pose Tracker Confirmed-Track Output Filter included in this PR. Status: Accepted.
Validation
New regression test
test_lost_tracks_excluded_from_bridge_output:Activeover 2 hits.loss_misses + 1cycles → track transitions toLost.tracker_update(empty)returnsVec::new().Losttrack is still intracker.all_tracks()(re-ID still works).ESP32-S3 on COM7 streamed unmodified firmware throughout — verified live CSI cb #32800, RSSI −46 dBm.
Test plan
cargo test --workspace --no-default-features→ 1,539 / 0test_tracker_update_stable_idsstill passes (Tentative→Active path unaffected)🤖 Generated with claude-flow