fix(meshcore): Discover Regions always timed out due to missing Sent payload#3702
Conversation
…ayload `sendToRadioFrame` fires the `Sent` event without a payload, unlike `sendTextMessage` which includes `expectedAckCrc`. The old `onSent` handler tried to read `r?.expectedAckCrc` from this payload-less event, leaving `tag = null`. The `onResp` guard `tag === null` then caused every `BinaryResponse` to be silently discarded, so the 15 s inner timer always fired and every repeater was skipped. Users saw "No regions found" even with a working repeater nearby. Fix: track `sentReceived` independently. When the Sent ack provides no usable tag (null or 0), accept the first `BinaryResponse` that arrives after the Sent ack — the requests are sequential so there is no concurrent reply to misidentify. When a non-zero tag is present (future firmware or sendBinaryRequest-style sends), the original tag-matching logic is preserved. Adds a regression test that simulates the payload-less Sent event and verifies the response is still resolved correctly. Fixes #3698 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017gqwwKYeyPEfZpMTWn5jX4
|
Claude finished @Yeraze's task —— View job PR Review
SummaryThis PR correctly identifies and fixes a real bug: Code Review (
|
Addresses code-review suggestion: the Sent ack fires exactly once per frame send, so `once` is correct. Using `on` left a window where a spurious duplicate Sent event could overwrite `tag` between the first ack and the BinaryResponse arriving. No behaviour change for the normal (single-fire) path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017gqwwKYeyPEfZpMTWn5jX4
|
Claude finished @Yeraze's task —— View job PR Review
SummaryThis PR correctly identifies and fixes a real bug: Code Review (
|
…ponse tag is unmatched (#3734) The #3702 fix assumed sendToRadioFrame fires Sent with no expectedAckCrc (tag = null → skip matching). In practice the firmware DOES populate expectedAckCrc (CRC of the outgoing ANON_REQ packet), but the repeater's BinaryResponse (0x8C) carries tag = 0 — a different scheme than SendBinaryReq (50), which explicitly echoes the CRC. With tag = non-zero CRC and BinaryResponse.tag = 0, the old guard: if (tag !== null && tag !== 0 && r?.tag !== tag) return; evaluates to true and silently drops every response, causing the 15-second inner timeout and "No regions found" for every user attempt (#3734). Fix: remove the tag-matching guard entirely. CMD_SEND_ANON_REQ (57) does not use CRC-echoing tag correlation; operations are serialized by runExclusiveRadioOp so the first BinaryResponse after sentReceived is guaranteed to be ours. The tag variable is also removed since it is no longer used. Updates: correct the stale runExclusiveRadioOp comment that claimed sendToRadioFrame fires Sent with no expectedAckCrc (it does). Test: add a regression test simulating the exact #3734 scenario — expectedAckCrc = 0xcafe1234 in Sent, tag = 0 in BinaryResponse — and verify the regions are resolved rather than timing out. Fixes #3734 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014DhSciCtki5JMrpUrKxwnj
…x region discovery timeout (#3719, #3734) (#3736) * fix: prevent messages from vanishing in multi-source unified feed (#3719) Two compounding bugs caused messages to disappear from the unified channel view ~60 seconds after arrival when multiple sources are connected. 1. fetchLimit starvation: the per-source window was limit×2 = 200 rows. A high-traffic MQTT bridge (e.g. a continental Canadaverse bridge at ~3 msg/s) pushes a message exclusive to that source beyond position 200 in ~60 s, making it invisible to the unified endpoint on the next poll. Raised to limit×5 = 500, giving ~167 s of headroom at 3 msg/s — well above the 10 s poll interval in all realistic deployments. 2. _dbchan/_radio suffix breaks dedup key: meshtasticManager inserts server-decrypted copies of a message with IDs ending in `_dbchan` or `_radio`. extractPacketIdFromRowId returned null for these because the last underscore-segment was not numeric, so those copies got a text+timestamp dedupKey instead of the correct packetId-based key. They appeared as duplicate entries or were sorted separately rather than merging with the originals receptions array. Fixed by stripping known server-copy suffixes before extracting the numeric packet ID. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014DhSciCtki5JMrpUrKxwnj * fix(meshcore): drop tag guard in request_regions — ANON_REQ BinaryResponse tag is unmatched (#3734) The #3702 fix assumed sendToRadioFrame fires Sent with no expectedAckCrc (tag = null → skip matching). In practice the firmware DOES populate expectedAckCrc (CRC of the outgoing ANON_REQ packet), but the repeater's BinaryResponse (0x8C) carries tag = 0 — a different scheme than SendBinaryReq (50), which explicitly echoes the CRC. With tag = non-zero CRC and BinaryResponse.tag = 0, the old guard: if (tag !== null && tag !== 0 && r?.tag !== tag) return; evaluates to true and silently drops every response, causing the 15-second inner timeout and "No regions found" for every user attempt (#3734). Fix: remove the tag-matching guard entirely. CMD_SEND_ANON_REQ (57) does not use CRC-echoing tag correlation; operations are serialized by runExclusiveRadioOp so the first BinaryResponse after sentReceived is guaranteed to be ours. The tag variable is also removed since it is no longer used. Updates: correct the stale runExclusiveRadioOp comment that claimed sendToRadioFrame fires Sent with no expectedAckCrc (it does). Test: add a regression test simulating the exact #3734 scenario — expectedAckCrc = 0xcafe1234 in Sent, tag = 0 in BinaryResponse — and verify the regions are resolved rather than timing out. Fixes #3734 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014DhSciCtki5JMrpUrKxwnj * revert fetchLimit to 2x — client append-only feed (#3738) supersedes the window-widening The limit*5 widening in this PR was a mitigation for the #3719 disappearance: it kept a single-source-exclusive message inside the server window for ~167s. The client-side append-only accumulator merged in #3738 now makes a displayed message persist regardless of how the server window moves, at any traffic rate, so the extra fetch headroom is redundant and only adds per-poll cost. Restore the original 2x (same-packet dedup headroom). The _dbchan/_radio dedup-key fix in this PR is a separate correctness bug and is kept. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJPe6J5vKrcbwzt6vCdtrt --------- Co-authored-by: Claude <noreply@anthropic.com>
Summary
Fixes #3698 — "Discover regions from repeater" never returned any regions even with a working nearby repeater.
sendToRadioFramefires theSentevent without a payload. Therequest_regionshandler expectedexpectedAckCrcin that payload (likesendTextMessageprovides), leavingtag = nullpermanently. EveryBinaryResponsewas then discarded by thetag === nullguard, the 15 s inner timeout fired silently, and all repeaters were skipped.sentReceivedas a separate boolean. Whentagis null/0 (no usable CRC from the Sent ack), accept the firstBinaryResponseafter the Sent ack fires. When a non-zero tag is present, the original tag-matching logic is preserved.Sentevent and verifies the regions response is still resolved correctly.Test plan
npm test— existingrequest_regionstest still passes (tag-matching path exercised by the mock'sexpectedAckCrc: 0x99)request_regions succeeds when Sent ack carries no expectedAckCrcpasses🤖 Generated with Claude Code
https://claude.ai/code/session_017gqwwKYeyPEfZpMTWn5jX4
Generated by Claude Code