fix(meshcore): correlate buffered SNR/route to the matching packet for auto-ack {SNR}/{ROUTE} (#3589)#3592
Conversation
… packet (#3589) The {SNR} and {ROUTE} auto-ack tokens for MeshCore companion messages were intermittently empty because the per-packet SNR and relay-hash chain are NOT carried on the ContactMsgRecv/ChannelMsgRecv events — they arrive only on the separate LogRxData push, buffered in a single slot and consumed by the next message recv. That single-slot correlation was fragile: - a room post (also a TXT_MSG) buffered a path but returned early WITHOUT consuming it, so its SNR/route leaked onto the next unrelated message; - a stale buffer from a packet whose recv never arrived (e.g. a non-TXT packet, or LogRxData with no following recv) could be attached to a later message, or left dangling. Add two correlation guards in a shared consumePendingPath() helper used by both recv handlers and the room-post path: 1. Freshness — reject a buffer older than PENDING_PATH_MAX_AGE_MS (500ms); LogRxData fires on the same I/O tick as its matching recv. 2. pathLen match — the recv's packed pathLen byte must equal the buffered packet's rawPathLen; a mismatch proves it's a different packet. The buffer is always cleared on consume (consume-once), and the room post now consumes/discards its own buffer so it can't leak forward. Adds tests for pathLen mismatch, staleness, consume-once, and the room-post leak. Closes #3589 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011JEaCGwY9Wz8jeV4e22GW4
|
Claude finished @Yeraze's task —— View job PR Review — fix(meshcore): correlate buffered SNR/route to the matching packet (#3589)
SummaryThis is a well-reasoned fix for a real protocol-level problem. The root cause analysis is accurate, the implementation is clean and testable, and the new tests directly exercise the failure modes that caused the original bug. Here's the detailed review: Code Quality & Design
Room post fix ( Potential Issue: pathLen correlation edge case
if (
typeof msgPathLen === 'number' &&
typeof buffered.rawPathLen === 'number' &&
msgPathLen !== buffered.rawPathLen
) {
// Buffer is from a different packet (packed pathLen bytes differ).
return undefined;
}When Minor observations
Test CoverageThe 4 new test cases in
The fake timer test correctly uses Gap to consider: There's no test for a Security & PerformanceNo security concerns. No performance concerns — the single-slot buffer and lightweight timestamp check are negligible overhead. VerdictApprove. The fix is correct, minimal, well-documented, and the new tests are targeted at the exact failure modes. The |
Problem
{SNR}and{ROUTE}in MeshCore companion auto-acknowledge templates were intermittently empty for both DMs and channel messages (issue #3589) — direct-hop messages dropped{SNR}, multi-hop dropped both, with no apparent pattern.Root cause
The per-packet SNR and the relay-hash chain are not delivered on the MeshCore
ContactMsgRecv/ChannelMsgRecvevents (confirmed inmeshcore.js: those events only carrypathLen, no SNR, no hashes). They arrive on a separateLogRxDatapush, which the firmware emits immediately before the matching txt-msg recv. The backend buffered that data in a single slot (pendingTxtMsgPath) and consumed it on the next recv.That single-slot correlation was fragile and produced the intermittent loss:
TXT_MSG, so the precedingLogRxDatabuffered a path for it, but the room-post handlerreturned early without consuming the buffer. The stale SNR/route then leaked onto the next unrelated DM/channel message.LogRxDatafired but its recv never arrived (a non-TXT packet, or raw logging gaps), the buffer lingered and could be attached to a later, unrelated message.Fix
Introduce a shared
consumePendingPath(msgPathLen)helper used by both recv handlers and the room-post path. It always clears the buffer (consume-once) and attaches the buffered SNR/hops only when both guards pass:PENDING_PATH_MAX_AGE_MS(500 ms).LogRxDataand its matching recv land on the same I/O tick; an older buffer belongs to a packet whose recv never arrived.pathLenbyte must equal the buffered packet'srawPathLen(same wire byte for the same packet); a mismatch proves it's a different packet.When the data genuinely isn't present, the tokens degrade as before (
{SNR}→—,{ROUTE}→direct/—), but they no longer get a wrong value from an unrelated packet, and the leak that silently blanked them is eliminated.Tests
Added to
meshcoreNativeBackend.otaPacket.test.ts:pathLendiffers (different packet)LogRxDatagets nothingExisting SNR/path correlation tests still pass.
Verification
success: true)tsc --noEmit: no new errors introduced (pre-existing repo-wide frontend errors unchanged at 61)Closes #3589
🤖 Generated with Claude Code