fix(neighbors): align live map "Show Neighbor Info" with the Map Analysis Neighbors view#3560
Conversation
…Neighbors view The live source map and the Map Analysis page disagreed on which neighbor links to draw. Both read the same neighbor_info table, but applied different freshness filters: - Map Analysis "Neighbors" (/api/analysis/neighbors) windows by the NeighborInfo record's own `timestamp` (report recency). - The live map (/api/sources/:id/neighbor-info and the legacy /api/neighbor-info) gated on the endpoint *nodes'* `lastHeard`, so a node's stale last-known neighbor list kept showing for as long as the node stayed otherwise active (still sending position/telemetry). That surfaced far more links than the analysis view and misrepresented how recently each RF link was actually observed. Switch the live-map endpoints to report-time freshness (record `timestamp` within maxNodeAge), matching the analysis definition. Because each NeighborInfo packet deletes-then-reinserts the reporter's full list with a fresh timestamp, a fresh record inherently means we heard the reporter within the window. Keying off the record timestamp (instead of node `lastHeard`) also naturally preserves indirect-neighbor links whose neighbor row has a null lastHeard (#3025/#2615), since lastHeard is no longer consulted. Firmware-supplied `lastRxTime` is also dropped from the predicate: it is subject to device clock skew and the analysis view does not use it, so it could otherwise reintroduce divergence. Tests updated to assert report-time semantics (stale report dropped even when endpoint nodes are still active; fresh report kept even when a node's lastHeard is stale/null). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SVGxkuD4Fwa2JGVim8ZeVj
|
Claude finished @Yeraze's task —— View job PR Review: fix(neighbors): align live map "Show Neighbor Info" with Map Analysis Neighbors view
SummaryThe fix is well-motivated and correctly implemented. Both endpoints now filter neighbor edges using the NeighborInfo record's Code Quality
The new filter is clean and correct. One minor ordering note: the
The same logic is applied consistently. Note that this file calls
Both endpoints assume Null-safety on
Potential Issue: filter order causes unnecessary enrichmentIn Test CoverageThe updated tests in
The Minor Observations
VerdictApprove. The root cause is correctly identified and the fix is minimal, consistent, well-commented, and well-tested. No bugs or security issues introduced. The pre-existing key-name discrepancy ( |
|
Thanks for the review. All three observations are non-blocking and I'm leaving the code as-is:
Ready to merge from my side once you're happy. |
Problem
A user reported (via Discord) that the Neighbors drawn on the Map Analysis page don't match the Show Neighbor Info links on the main source map — even with Show Route Segments and Show Traceroute disabled. The live map showed many more links than the analysis view.
Root cause
Both views read the same
neighbor_infotable (each NeighborInfo packet deletes-then-reinserts the reporter's full list with a freshtimestamp, so the table holds each reporter's latest list). They diverged purely in the freshness filter:/api/analysis/neighborstimestampwithin lookback (report recency)/api/sources/:id/neighbor-info(+ legacy/api/neighbor-info)lastHeardwithinmaxNodeAgeHoursSo the live map kept showing a node's stale last-known neighbor list for as long as the node stayed otherwise active (still sending position/telemetry), surfacing far more links than the analysis view and misrepresenting how recently each RF link was observed.
Fix
Per maintainer decision (report-time is the canonical "neighbor" definition), switch the live-map endpoints to report-time freshness — keep an edge only when its NeighborInfo record
timestampis withinmaxNodeAgeHours— matching the analysis view.node.lastHeardgate loses no real signal.lastHeard([BUG] Meshtastic NeigborInfo: Response received but not shown in UI #3025/fix(virtual-node): stop creating zombie nodes on map (#2602) #2615), sincelastHeardis no longer consulted — the prior special-case fallback is now the default behavior.lastRxTimeis dropped from the predicate (device-clock-skew prone; the analysis view doesn't use it, so it would reintroduce divergence).Applied to both
/api/sources/:id/neighbor-info(used by the live map) and the legacy/api/neighbor-infofor consistency.Tests
sourceRoutes.neighbor-info.test.tsto assert report-time semantics: a stale report is dropped even when endpoint nodes are still active; a fresh report is kept even when a node'slastHeardis stale/null (locks in the alignment direction). [BUG] Meshtastic NeigborInfo: Response received but not shown in UI #3025 null-neighbor and [BUG] MeshCore telemetry retrieval fails on every repeater (meshcore-only setup, v4.6.1) #3092 channel-permission cases retained.server.neighbor-info-position.test.ts.Notes / possible follow-up
maxNodeAgeHoursfor the live map vs the analysis page'sLOOKBACKselector); set both to the same value to compare like-for-like. Semantics (the filtered field) now match.viewOnMapfiltering. For non-admins this can still differ — out of scope here, flagging for a possible future pass.🤖 Generated with Claude Code