Skip to content

fix: MeshCore SoftAP TX, hop goblins, Reticulum BLE bond desync - #808

Merged
rinchen merged 6 commits into
mainfrom
liam
Aug 6, 2026
Merged

fix: MeshCore SoftAP TX, hop goblins, Reticulum BLE bond desync#808
rinchen merged 6 commits into
mainfrom
liam

Conversation

@rinchen

@rinchen rinchen commented Aug 6, 2026

Copy link
Copy Markdown
Member

Summary

Multi-fix branch covering MeshCore SoftAP dead-bridge chat TX, MeshCore hop-goblin false positives (#804), Reticulum BLE RNode bond desync / TX-drop guidance, and meshcore.js patch cleanup after upstream closures.

MeshCore SoftAP / OpenHop dead-bridge TX (fec0c830)

  • SoftAP companions often FIN the TCP bridge right after getContacts, leaving a configured session with a dead socket — chat TX then fails while reconnect thrashing tries to “fix” it.
  • New meshcoreTcpInitBurst coordination: keep SoftAP/OpenHop configured after contacts FIN, reopen a live TCP window for user sends, and let initConn release SoftAP user-TX waiters after getSelfInfo so sends complete before the contacts dump/FIN race.
  • useSendMessage + useMeshcoreRuntime / conn side effects wired to wait on that live window instead of treating the dead bridge as a full reconnect loop.
  • Also lands remaining 24h-review hardening: i18n quality checks, docs/troubleshooting, Reticulum bounds (games outbound store, LXMF delivery, sidecar issue tracker sticky enabled-set, DB handler guards).

MeshCore hop goblins (227242c3) — Fixes #804

  • MeshCore nearby multi-hop contacts are poorly connected, not Meshtastic-style GPS+hop over-hopping.
  • New capability hasDistanceBasedHopAnomalies (Meshtastic true; MeshCore/Reticulum false).
  • RoutingDiagnosticEngine skips hop_goblin and close-in bad_route when the capability is false.
  • Docs + capability tests updated.

Reticulum BLE RNode bond desync / TX-drop guidance (5f618c38)

  • Stop precipitating stale OS bonds: graceful prepare-stop, sleep/wake detach, bond-aware reconnect.
  • Cleaner recovery: halt Noble yield appropriately, USB unpair path in flasher/Admin, transport-aware TX hints.
  • New rsReticulum overlay rsReticulum-ble-rnode-bond-desync.patch + apply script / update.sh watch entry.
  • UI: ReticulumSidecarIssueAlertsBlock bond-removed actions (Stop stack / Open Admin Bluetooth), local interface health helpers, diagnostic engine coverage, i18n across locales.

meshcore.js patch hygiene

Commits (vs main)

  1. ee53b8e9 — chore: drop redundant meshcore.js empty-password patch hunk
  2. 256baf9f — chore: drop unused meshcore.js LoginFail patch hunk
  3. 227242c3 — fix: suppress MeshCore hop goblin diagnostics (Fixes #804)
  4. 5f618c38 — fix(reticulum): harden BLE RNode bond desync and TX-drop guidance
  5. fec0c830 — fix(meshcore): recover SoftAP dead-bridge chat TX without reconnect thrash

Scope: ~87 files, +2430 / −431 (includes locales, docs, sidecar overlay, tests).

Test plan

  • MeshCore SoftAP/OpenHop: connect over TCP SoftAP; confirm configure completes; after companion FIN post-contacts, chat send recovers via live-window reopen without reconnect thrash / unconfigure flicker
  • MeshCore SoftAP: send during init burst (before/around getContacts) still lands; no stuck “sending” when bridge dies after contacts dump
  • MeshCore diagnostics (MeshCore hop goblins #804): with nearby multi-hop MeshCore contacts + GPS distance, confirm no Hop Goblin / close-in bad_route rows; Meshtastic still shows hop goblins when applicable
  • Reticulum BLE RNode bond desync: reproduce stale OS “Paired” bond → issue banner + Stop stack / Open Admin Bluetooth actions; Forget in OS Bluetooth → re-pair PIN path works
  • Reticulum sleep/wake: BLE RNode detach/reconnect does not leave Noble yield stuck; Meshtastic/MeshCore BLE can reconnect after yield release
  • Reticulum TX drops: transport-aware hints for BLE vs TCP/USB when outbound fails
  • RNode flasher USB: Clear paired devices / Start pairing still works after bond-desync UI wiring
  • meshcore.js: empty-password login and room/repeater login still behave (no regression from dropped patch hunks)
  • pnpm run check:pr (or at least lint + typecheck + test:run for touched areas); sidecar pnpm run check:reticulum-sidecar if cargo available
  • Spot-check new i18n keys in a non-English locale (bond-removed banner, SoftAP-related strings if any)

Notes

Summary by CodeRabbit

  • New Features
    • Added Bluetooth administration actions to clear paired devices, with confirmation prompts.
    • Added clearer Reticulum connection and diagnostic guidance, including BLE-specific recovery steps.
    • Improved MeshCore TCP message delivery by recovering dead connections when possible.
    • Added graceful shutdown handling for Reticulum Bluetooth connections.
  • Bug Fixes
    • Prevented unsupported routing warnings from appearing for MeshCore.
    • Improved TCP reliability, keepalive behavior, and oversized-message protection.
    • Fixed duplicate pending message cleanup and safer message replacement handling.
    • Improved BLE bond-desynchronization detection and reconnect behavior.
  • Documentation
    • Expanded troubleshooting, accessibility, appearance customization, and reconnection guidance.

rinchen added 5 commits August 6, 2026 06:58
Upstream #29 was closed as unnecessary — writeString("") already emits
zero password bytes. Keep #30#33 and local silence hunks; refresh patch
hash and README sunset tracking.
Upstream closed #32; stock firmware does not push 0x86 on bad credentials
(client timeout), so stop carrying the dead local handler.
Skip GPS+hop distance anomalies (hop_goblin, close-in bad_route)
when hasDistanceBasedHopAnomalies is false. MeshCore nearby multi-hop
contacts are poorly connected, not Meshtastic-style over-hopping.

Fixes #804
Stop precipitating stale OS bonds (graceful prepare-stop, sleep/wake detach, bond-aware reconnect) and recover cleaner (halt Noble, USB unpair, transport-aware TX hints).
…hrash

Keep SoftAP/OpenHop configured after contacts FIN, reopen a live TCP window for user sends, and land the remaining 24h-review hardening (i18n, docs, Reticulum bounds).
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@rinchen, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 27 minutes

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

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: 0d4c56df-47c6-4014-a16d-22cb8fd82181

📥 Commits

Reviewing files that changed from the base of the PR and between fec0c83 and 55288e2.

📒 Files selected for processing (17)
  • scripts/check-i18n-quality.mjs
  • src/main/index.ipc-security.test.ts
  • src/main/reticulum-sidecar-manager.test.ts
  • src/main/reticulum-sidecar-manager.ts
  • src/renderer/components/ReticulumSidecarIssueAlertsBlock.tsx
  • src/renderer/components/reticulum/ReticulumInterfacesPanel.test.tsx
  • src/renderer/lib/flasher/rnode.test.ts
  • src/renderer/lib/meshcore/meshcoreTcpInitBurst.ts
  • src/renderer/lib/reticulum/reticulumBleBondDesync.test.ts
  • src/renderer/lib/reticulum/reticulumNobleBleYield.test.ts
  • src/renderer/lib/reticulum/reticulumNobleBleYield.ts
  • src/renderer/lib/reticulum/useReticulumNobleBleYieldWatcher.test.ts
  • src/renderer/lib/reticulum/useReticulumNobleBleYieldWatcher.ts
  • src/renderer/runtime/useMeshcoreRuntime.reconnect.test.ts
  • src/renderer/runtime/useMeshcoreRuntime.ts
  • src/renderer/runtime/useReticulumRuntime.reconnect-hardening.test.ts
  • src/renderer/runtime/useReticulumRuntime.ts
📝 Walkthrough

Walkthrough

This PR adds orderly Reticulum shutdown, BLE bond recovery, transport and persistence safeguards, protocol-aware diagnostics, and MeshCore TCP dead-bridge recovery. It also updates related documentation, tests, scripts, and UI actions.

Changes

Reliability and recovery updates

Layer / File(s) Summary
Documentation and operational contracts
AGENTS.md, ARCHITECTURE.md, CONTRIBUTING.md, README.md, docs/*
Documents updated appearance controls, reconnect ownership, protocol diagnostic limits, BLE recovery, schema upgrades, and message replacement behavior.
Orderly sidecar shutdown
reticulum-sidecar/src/api/*, reticulum-sidecar/src/stack/{live,mod}.rs, src/main/reticulum-sidecar-manager.*
Adds /api/v1/stack/prepare-stop, waits for runtime shutdown, and coordinates best-effort preparation before SIGTERM.
Transport and persistence guards
reticulum-sidecar/src/stack/*, src/main/index.ts, src/main/ipc/*, src/renderer/lib/ingest/*
Bounds outbound envelopes and queues, rejects oversized TCP input, validates replacement hashes, and preserves atomic LXMF replacement metadata.
BLE bond remediation
scripts/*, src/renderer/lib/flasher/*, src/renderer/components/flasher/*
Adds the bond-desynchronization overlay, radio unpair command, confirmation UI, and translation-quality checks.
BLE bond runtime and diagnostics
src/main/reticulumSidecarIssueTracker.*, src/renderer/lib/reticulum/*, src/renderer/runtime/useReticulumRuntime.ts, src/renderer/components/Reticulum*
Tracks sticky bond-removal alerts, blocks Noble reacquisition, classifies queue-drop causes, and exposes stack-stop and Bluetooth administration actions.
Protocol-aware routing diagnostics
src/renderer/lib/radio/*, src/renderer/lib/diagnostics/RoutingDiagnosticEngine.*
Adds hasDistanceBasedHopAnomalies and suppresses unsupported distance-based findings for MeshCore and Reticulum.
MeshCore TCP dead-bridge recovery
src/renderer/lib/meshcore/*, src/renderer/runtime/useMeshcoreRuntime*, src/renderer/hooks/useSendMessage.*, src/renderer/hooks/meshcore/*
Adds SoftAP dead-session tracking, user-transmission waiters, runtime-owned TCP recovery, and refreshed handles for recovered sends.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant ReticulumSidecarManager
  participant ReticulumSidecar
  participant LiveBridge
  App->>ReticulumSidecarManager: stop sidecar
  ReticulumSidecarManager->>ReticulumSidecar: POST /api/v1/stack/prepare-stop
  ReticulumSidecar->>LiveBridge: prepare_stop()
  LiveBridge-->>ReticulumSidecar: shutdown complete
  ReticulumSidecar-->>ReticulumSidecarManager: success or error
  ReticulumSidecarManager->>ReticulumSidecar: SIGTERM
Loading
sequenceDiagram
  participant useSendMessage
  participant MeshcoreSessionApi
  participant useMeshcoreRuntime
  participant MeshCoreTCP
  useSendMessage->>MeshcoreSessionApi: ensureTcpLiveForUserTx()
  MeshcoreSessionApi->>useMeshcoreRuntime: recover dead bridge
  useMeshcoreRuntime->>MeshCoreTCP: reconnect and await self information
  MeshCoreTCP-->>useMeshcoreRuntime: live bridge
  useMeshcoreRuntime-->>MeshcoreSessionApi: ready for transmission
  MeshcoreSessionApi-->>useSendMessage: refreshed handle
  useSendMessage->>MeshCoreTCP: send message
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 63.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the three primary fixes: MeshCore SoftAP transmission, hop-goblin diagnostics, and Reticulum BLE bond desynchronization.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch liam

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.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 5

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/renderer/lib/reticulum/useReticulumNobleBleYieldWatcher.ts (1)

100-106: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Cancel or serialize stale Noble-yield synchronization before bond-desync cleanup.

A tick that started before bondDesyncActive can pass its cancellation check, await syncReticulumNobleBleYield(), and later prepare or retain the Reticulum lease. A newer bond-desync tick releases that lease, but the older tick can reacquire it after the release. This violates the no-reacquire guarantee during stale-bond recovery.

  • src/renderer/lib/reticulum/useReticulumNobleBleYieldWatcher.ts#L100-L106: Give each active effect an AbortController, abort it during cleanup, and pass its signal into synchronization.
  • src/renderer/lib/reticulum/reticulumNobleBleYield.ts#L46-L64: Serialize competing synchronizations or enforce cancellation after every awaited lease operation before mutating yieldActive or acquiring a lease.
  • src/renderer/lib/reticulum/reticulumNobleBleYield.test.ts#L266-L301: Add a deferred-promise regression test where a normal tick starts first, bond desync activates, and the first tick resolves afterward.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/lib/reticulum/useReticulumNobleBleYieldWatcher.ts` around lines
100 - 106, Prevent stale Noble-yield synchronizations from reacquiring the
Reticulum lease after bond-desync cleanup. In
src/renderer/lib/reticulum/useReticulumNobleBleYieldWatcher.ts:100-106, create
an AbortController per active effect, abort it during cleanup, and pass its
signal to syncReticulumNobleBleYield. In
src/renderer/lib/reticulum/reticulumNobleBleYield.ts:46-64, serialize competing
synchronizations or check cancellation after every awaited lease operation
before changing yieldActive or acquiring a lease. In
src/renderer/lib/reticulum/reticulumNobleBleYield.test.ts:266-301, add a
deferred-promise regression test where a normal tick starts before bond desync
and resolves afterward, verifying the stale tick cannot reacquire the lease.

Source: Path instructions

reticulum-sidecar/src/stack/lxmf_delivery.rs (1)

284-323: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bound the outbound backchannel ingress too.

spawn_lxmf_outbound_backchannel() builds an unbounded outer_tx and then drops messages only after forwarding them to the 256-item inner_tx. A producer that outpaces delivery will still build an unbounded backlog in outer_rx; use a bounded admission step and add a saturation test with delivery blocked and producer traffic flooded.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@reticulum-sidecar/src/stack/lxmf_delivery.rs` around lines 284 - 323, Bound
admission in spawn_lxmf_outbound_backchannel instead of using an unbounded
outer_tx, so producer traffic is dropped once the configured capacity is reached
rather than accumulating in outer_rx. Preserve the existing drop-newest behavior
and saturation warning, and add a test that blocks delivery, floods the
producer, and verifies the ingress queue remains bounded.

Source: Path instructions

src/renderer/runtime/useMeshcoreRuntime.ts (1)

2868-2876: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

SoftAP dead-bridge state is not cleared when switching away from TCP.

meshcoreTcpBridgeDeadRef.current and setMeshcoreTcpSoftApDeadAccepted(false) reset only inside if (type === 'tcp'). If a TCP SoftAP session reaches the dead-bridge-accepted state and the user then connects via BLE or serial, prepareRfConnect('ble'|'serial', ...) skips this block, so both flags stay stale.

useSendMessage.ts gates its SoftAP recovery branch only on isMeshcoreTcpSoftApDeadAccepted(), with no transport check. On the next send over the new BLE/serial session, that stale flag routes the send into ensureTcpLiveForUserTx(). Since meshcoreConnectTypeRef.current is no longer 'tcp', ensureTcpLiveForUserTx() calls handleMeshcoreConnectionLostRef.current() — forcing an unnecessary reconnect of the healthy BLE/serial session — then waits on waitForMeshcoreTcpLiveForUserTx(), which only resolves through the TCP-only notifyMeshcoreTcpLiveForUserTx() call in initConn. The wait always times out after MESHCORE_TCP_USER_TX_LIVE_TIMEOUT_MS (20s), and the message is marked failed. This repeats for every subsequent send until the app restarts.

Move the TCP-specific flag resets outside the type === 'tcp' guard so they clear for every transport.

🐛 Proposed fix: reset SoftAP/dead-bridge state for every transport
-      if (type === 'tcp') {
-        meshcoreTcpBridgeDeadRef.current = false;
-        meshcoreTcpInitBurstCapturedRef.current = false;
-        meshcoreTcpContactsDumpInFlightRef.current = false;
-        setMeshcoreTcpSoftApDeadAccepted(false);
-        if (!opts?.preserveReconnectState) {
-          meshcoreDeferredReconnectRef.current = false;
-        }
-      }
+      meshcoreTcpBridgeDeadRef.current = false;
+      meshcoreTcpInitBurstCapturedRef.current = false;
+      meshcoreTcpContactsDumpInFlightRef.current = false;
+      setMeshcoreTcpSoftApDeadAccepted(false);
+      if (type === 'tcp' && !opts?.preserveReconnectState) {
+        meshcoreDeferredReconnectRef.current = false;
+      }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/runtime/useMeshcoreRuntime.ts` around lines 2868 - 2876, Move
the resets for meshcoreTcpBridgeDeadRef.current and
setMeshcoreTcpSoftApDeadAccepted(false) out of the type === 'tcp' guard in
prepareRfConnect so they execute for every transport. Keep the TCP-only burst,
contacts, and deferred reconnect resets inside the TCP guard, preserving the
existing preserveReconnectState behavior.
🟡 Other comments (7)
src/renderer/lib/reticulum/reticulumBleBondDesync.test.ts-14-24 (1)

14-24: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Exercise the unsubscribe contract.

After unsub(), trigger another state transition. Assert that seen does not change. The current test passes if unsub() does nothing.

Proposed fix
     setReticulumBleBondDesyncActive(true);
     setReticulumBleBondDesyncActive(true);
     setReticulumBleBondDesyncActive(false);
     unsub();
+    setReticulumBleBondDesyncActive(true);
     expect(seen).toEqual([true, false]);

As per coding guidelines, “Ship a passing test for behavioral changes before considering the work complete.” As per path instructions, “Prefer behavioral assertions; skip style-only test nits.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/lib/reticulum/reticulumBleBondDesync.test.ts` around lines 14 -
24, Update the test “notifies subscribers when the flag changes” to trigger an
additional Reticulum bond desync state transition after calling unsub(), then
assert that seen remains [true, false]. Keep the existing pre-unsubscribe
notification assertions unchanged so the test verifies both subscription
behavior and the unsubscribe contract.

Sources: Coding guidelines, Path instructions

src/renderer/components/flasher/RNodeFlasherSection.tsx-571-573 (1)

571-573: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a renderer test for the clear paired devices flow.

Cover opening the confirmation modal, canceling without calling clearBluetoothBonds(), confirming once with runWithRNode, and reporting the flasher.clearPairedDevicesSuccess status. Include an axe assertion for the modal’s interactive role, because this new direct action changes UI behavior; the existing component-level test does not cover this workflow.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/components/flasher/RNodeFlasherSection.tsx` around lines 571 -
573, Add a renderer test for the clear paired devices flow in
RNodeFlasherSection that exercises the onClearPairedDevices path: verify it
opens the confirmation modal, canceling does not call clearBluetoothBonds(),
confirming calls clearBluetoothBonds() once through runWithRNode, and the
success path reports flasher.clearPairedDevicesSuccess. Include an axe assertion
for the modal’s interactive role, and extend the existing component-level
coverage rather than changing the implementation.

Sources: Coding guidelines, Path instructions

src/renderer/components/reticulum/ReticulumInterfacesPanel.tsx-2138-2142 (1)

2138-2142: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add behavioral coverage for the BLE transport hint.

ReticulumInterfacesPanel.test.tsx does not assert the rnodeTransportBleHint message behavior. Add a passing test that renders the hint for BLE transport and omitted it for serial and Wi-Fi transports before considering this change complete.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/components/reticulum/ReticulumInterfacesPanel.tsx` around lines
2138 - 2142, Add behavioral coverage in ReticulumInterfacesPanel.test.tsx for
the rnodeTransport conditional: assert rnodeTransportBleHint is rendered when
the transport is BLE and absent for both serial and Wi-Fi transports. Use the
existing panel render helpers and translation/query conventions.

Sources: Coding guidelines, Path instructions

scripts/check-i18n-quality.mjs-446-447 (1)

446-447: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the bond false-friend expression Unicode-safe.

FLASHER_BT_BOND_FINANCIAL_FALSE_FRIENDS uses \b, which does not create boundaries for Cyrillic or CJK terms. Translate values such as облигаци, 債券, and 债券 as plain strings outside the ASCII \b groups so these non-Latin financial bonds are caught.

Proposed fix
 export const FLASHER_BT_BOND_FINANCIAL_FALSE_FRIENDS =
-  /\b(Bonos|obligaciones|obligaties|Obligationen|облигаци|債券|债券|tahvil|obligacje|cautionnement|títulos de rádio|radiobonos)\b/i;
+  /(?:\b(?:Bonos|obligaciones|obligaties|Obligationen|tahvil|obligacje|cautionnement|títulos de rádio|radiobonos)\b|облигаци|債券|债券)/iu;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/check-i18n-quality.mjs` around lines 446 - 447, Update
FLASHER_BT_BOND_FINANCIAL_FALSE_FRIENDS so non-Latin terms such as облигаци, 債券,
and 债券 are matched as standalone plain-string alternatives rather than relying
on ASCII \b boundaries; retain the existing boundary-wrapped handling for Latin
terms and preserve case-insensitive matching.
src/renderer/components/ConnectionPanel.tsx-351-352 (1)

351-352: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a ConnectionPanel regression test for Bluetooth administration routing.

Render the Reticulum connection path, raise a stale BLE-bond alert, click the Open Admin Bluetooth action, and assert that onOpenAdminBluetooth runs. The current component tests validate the alert block in isolation. They do not validate this new prop path.

As per coding guidelines, ship a passing test for behavioral changes.

Also applies to: 373-373, 3094-3094

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/components/ConnectionPanel.tsx` around lines 351 - 352, Add a
ConnectionPanel regression test covering the Reticulum connection path: render
it with onOpenAdminBluetooth mocked, trigger the stale BLE-bond alert, click the
Open Admin Bluetooth action, and assert the callback executes. Keep the existing
isolated alert tests unchanged while validating the prop path through the
rendered component.

Source: Coding guidelines

src/main/reticulum-sidecar-manager.test.ts-674-681 (1)

674-681: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert prepare-stop before SIGTERM.

This test finds the POST and SIGTERM independently. It still passes if a future change sends SIGTERM before the request.

Record the call sequence and assert that the prepare-stop request occurs before proc.kill('SIGTERM').

As per path instructions, prefer behavioral assertions in .test.ts files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/reticulum-sidecar-manager.test.ts` around lines 674 - 681, Update
the stop-order test around manager.stop() to record the invocation sequence for
the prepare-stop fetch request and proc.kill, then assert that the POST to
/api/v1/stack/prepare-stop occurs before proc.kill('SIGTERM'). Keep the existing
method and call assertions while making ordering part of the behavioral
verification.

Sources: Coding guidelines, Path instructions

src/main/index.ts-6302-6314 (1)

6302-6314: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add regression coverage for oversized Meshtastic TCP chunks.

The meshtastic:tcp-data data handler drops an oversized chunk and destroys the socket, but no main-process behavior test exercises that path. Add a test that emits a chunk larger than MESHTASTIC_TCP_DATA_MAX_BYTES and asserts destruction, plus no meshtastic:tcp-data payload.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/index.ts` around lines 6302 - 6314, Add regression coverage for the
meshtastic:tcp-data data handler’s oversized-chunk branch: emit a chunk
exceeding MESHTASTIC_TCP_DATA_MAX_BYTES, assert the associated socket is
destroyed, and verify no meshtastic:tcp-data payload is emitted. Reuse the
existing main-process IPC test setup and handler symbols rather than adding
unrelated coverage.

Sources: Coding guidelines, Path instructions

🧹 Nitpick comments (1)
src/renderer/lib/meshcore/meshcoreTcpInitBurst.ts (1)

132-144: 🩺 Stability & Availability | 🔵 Trivial | 🏗️ Heavy lift

Fragile fixed-tick coupling between yieldToMeshcoreTcpUserTxSends and ensureTcpLiveForUserTx.

yieldToMeshcoreTcpUserTxSends waits exactly two Promise.resolve() ticks before snapshotting inFlightUserTxSends. This count only works because useMeshcoreRuntime.ts's ensureTcpLiveForUserTx resolves the waiter through exactly two async hops before trackMeshcoreTcpUserTxSend runs (waiter resolve → ensureTcpLiveForUserTx return → useSendMessage.ts resume → track). Add or remove a single await anywhere in that chain and the snapshot silently misses the in-flight send, reintroducing the SoftAP FIN race this PR is meant to close. No test currently exercises the full ensureTcpLiveForUserTxuseSendMessage.ts path together with yieldToMeshcoreTcpUserTxSends (existing tests call trackMeshcoreTcpUserTxSend directly).

Add a code comment documenting the exact tick-count dependency, or replace the fixed-tick wait with an explicit signal (for example, have ensureTcpLiveForUserTx itself register the send before resolving) so future changes cannot silently break this ordering.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/lib/meshcore/meshcoreTcpInitBurst.ts` around lines 132 - 144,
Make the ordering between yieldToMeshcoreTcpUserTxSends and
ensureTcpLiveForUserTx explicit rather than relying on an undocumented two-tick
delay: preferably register the user send before ensureTcpLiveForUserTx resolves
its waiter, or otherwise add a precise comment documenting the required
async-hop dependency. Ensure the snapshot still includes sends initiated through
the full ensureTcpLiveForUserTx → useSendMessage flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/reticulum-sidecar-manager.ts`:
- Around line 461-467: Update the watchdog restart path to use the tracked stop
operation exposed by stop() instead of calling stopProc() directly, ensuring
stopPromise remains set while shutdown is in progress and concurrent start()
calls cannot bypass the stop guard.

In `@src/renderer/components/ReticulumSidecarIssueAlertsBlock.tsx`:
- Around line 109-136: Update the onStopStack click handler in
ReticulumSidecarIssueAlertsBlock to handle rejected promises instead of
discarding them with void. Await or chain the action and log any failure with an
appropriate console debug, warn, or error call while preserving the existing
button behavior.

In `@src/renderer/lib/flasher/rnode.test.ts`:
- Around line 31-38: Replace the source-text assertions in the RNode Bluetooth
bond test with a behavioral test: create an RNode using createMockSerialPort(),
call clearBluetoothBonds(), and assert the serial port received the framed KISS
command containing RNode.CMD_BT_UNPAIR and 0x01.

In `@src/renderer/runtime/useReticulumRuntime.reconnect-hardening.test.ts`:
- Line 125: Replace the source-text regex assertions in the reconnect-hardening
tests with behavioral coverage of the mounted runtime. Mock Electron IPC,
exercise suspend and resume—including a forced fresh start—and assert stop, bond
release, status/IPC ordering, and message rekey effects. Update the related
assertions around the other referenced test block while preserving the existing
runtime setup.

In `@src/renderer/runtime/useReticulumRuntime.ts`:
- Around line 1524-1534: Update the coalescing branch in connect so a caller
requesting reuseIfRunning: false does not return after awaiting
connectInFlightDoneRef.current; once the pending flight settles, rerun
connect(opts) to start the required fresh connection, while preserving the
existing coalesced return for reusable calls. Add a regression test covering
suspend and resume during an in-flight connection.

---

Outside diff comments:
In `@reticulum-sidecar/src/stack/lxmf_delivery.rs`:
- Around line 284-323: Bound admission in spawn_lxmf_outbound_backchannel
instead of using an unbounded outer_tx, so producer traffic is dropped once the
configured capacity is reached rather than accumulating in outer_rx. Preserve
the existing drop-newest behavior and saturation warning, and add a test that
blocks delivery, floods the producer, and verifies the ingress queue remains
bounded.

In `@src/renderer/lib/reticulum/useReticulumNobleBleYieldWatcher.ts`:
- Around line 100-106: Prevent stale Noble-yield synchronizations from
reacquiring the Reticulum lease after bond-desync cleanup. In
src/renderer/lib/reticulum/useReticulumNobleBleYieldWatcher.ts:100-106, create
an AbortController per active effect, abort it during cleanup, and pass its
signal to syncReticulumNobleBleYield. In
src/renderer/lib/reticulum/reticulumNobleBleYield.ts:46-64, serialize competing
synchronizations or check cancellation after every awaited lease operation
before changing yieldActive or acquiring a lease. In
src/renderer/lib/reticulum/reticulumNobleBleYield.test.ts:266-301, add a
deferred-promise regression test where a normal tick starts before bond desync
and resolves afterward, verifying the stale tick cannot reacquire the lease.

In `@src/renderer/runtime/useMeshcoreRuntime.ts`:
- Around line 2868-2876: Move the resets for meshcoreTcpBridgeDeadRef.current
and setMeshcoreTcpSoftApDeadAccepted(false) out of the type === 'tcp' guard in
prepareRfConnect so they execute for every transport. Keep the TCP-only burst,
contacts, and deferred reconnect resets inside the TCP guard, preserving the
existing preserveReconnectState behavior.

---

Other comments:
In `@scripts/check-i18n-quality.mjs`:
- Around line 446-447: Update FLASHER_BT_BOND_FINANCIAL_FALSE_FRIENDS so
non-Latin terms such as облигаци, 債券, and 债券 are matched as standalone
plain-string alternatives rather than relying on ASCII \b boundaries; retain the
existing boundary-wrapped handling for Latin terms and preserve case-insensitive
matching.

In `@src/main/index.ts`:
- Around line 6302-6314: Add regression coverage for the meshtastic:tcp-data
data handler’s oversized-chunk branch: emit a chunk exceeding
MESHTASTIC_TCP_DATA_MAX_BYTES, assert the associated socket is destroyed, and
verify no meshtastic:tcp-data payload is emitted. Reuse the existing
main-process IPC test setup and handler symbols rather than adding unrelated
coverage.

In `@src/main/reticulum-sidecar-manager.test.ts`:
- Around line 674-681: Update the stop-order test around manager.stop() to
record the invocation sequence for the prepare-stop fetch request and proc.kill,
then assert that the POST to /api/v1/stack/prepare-stop occurs before
proc.kill('SIGTERM'). Keep the existing method and call assertions while making
ordering part of the behavioral verification.

In `@src/renderer/components/ConnectionPanel.tsx`:
- Around line 351-352: Add a ConnectionPanel regression test covering the
Reticulum connection path: render it with onOpenAdminBluetooth mocked, trigger
the stale BLE-bond alert, click the Open Admin Bluetooth action, and assert the
callback executes. Keep the existing isolated alert tests unchanged while
validating the prop path through the rendered component.

In `@src/renderer/components/flasher/RNodeFlasherSection.tsx`:
- Around line 571-573: Add a renderer test for the clear paired devices flow in
RNodeFlasherSection that exercises the onClearPairedDevices path: verify it
opens the confirmation modal, canceling does not call clearBluetoothBonds(),
confirming calls clearBluetoothBonds() once through runWithRNode, and the
success path reports flasher.clearPairedDevicesSuccess. Include an axe assertion
for the modal’s interactive role, and extend the existing component-level
coverage rather than changing the implementation.

In `@src/renderer/components/reticulum/ReticulumInterfacesPanel.tsx`:
- Around line 2138-2142: Add behavioral coverage in
ReticulumInterfacesPanel.test.tsx for the rnodeTransport conditional: assert
rnodeTransportBleHint is rendered when the transport is BLE and absent for both
serial and Wi-Fi transports. Use the existing panel render helpers and
translation/query conventions.

In `@src/renderer/lib/reticulum/reticulumBleBondDesync.test.ts`:
- Around line 14-24: Update the test “notifies subscribers when the flag
changes” to trigger an additional Reticulum bond desync state transition after
calling unsub(), then assert that seen remains [true, false]. Keep the existing
pre-unsubscribe notification assertions unchanged so the test verifies both
subscription behavior and the unsubscribe contract.

---

Nitpick comments:
In `@src/renderer/lib/meshcore/meshcoreTcpInitBurst.ts`:
- Around line 132-144: Make the ordering between yieldToMeshcoreTcpUserTxSends
and ensureTcpLiveForUserTx explicit rather than relying on an undocumented
two-tick delay: preferably register the user send before ensureTcpLiveForUserTx
resolves its waiter, or otherwise add a precise comment documenting the required
async-hop dependency. Ensure the snapshot still includes sends initiated through
the full ensureTcpLiveForUserTx → useSendMessage flow.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: 6cba939f-984f-480e-9013-14a5fb4cf7a3

📥 Commits

Reviewing files that changed from the base of the PR and between bd42368 and fec0c83.

⛔ Files ignored due to path filters (21)
  • patches/@liamcottle__meshcore.js@1.13.0.patch is excluded by !patches/**
  • patches/README.md is excluded by !patches/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !**/pnpm-lock.yaml
  • reticulum-sidecar/patches/README.md is excluded by !reticulum-sidecar/patches/**
  • reticulum-sidecar/patches/rsReticulum-ble-rnode-bond-desync.patch is excluded by !reticulum-sidecar/patches/**
  • src/renderer/locales/cs/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/de/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/en/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/es/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/fr/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/id/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/it/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/ja/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/ko/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/nl/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/pl/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/pt-BR/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/ru/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/tr/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/uk/translation.json is excluded by !src/renderer/locales/**
  • src/renderer/locales/zh/translation.json is excluded by !src/renderer/locales/**
📒 Files selected for processing (66)
  • AGENTS.md
  • ARCHITECTURE.md
  • CONTRIBUTING.md
  • README.md
  • docs/accessibility-checklist.md
  • docs/diagnostics.md
  • docs/meshcore-meshtastic-parity.md
  • docs/reticulum-games-parity.md
  • docs/reticulum-sidecar-ipc.md
  • docs/reticulum.md
  • docs/troubleshooting.md
  • reticulum-sidecar/src/api/mod.rs
  • reticulum-sidecar/src/api/system.rs
  • reticulum-sidecar/src/stack/games_outbound_store.rs
  • reticulum-sidecar/src/stack/live.rs
  • reticulum-sidecar/src/stack/lxmf_delivery.rs
  • reticulum-sidecar/src/stack/mod.rs
  • scripts/apply-rsReticulum-ble-rnode-bond-desync.sh
  • scripts/check-i18n-quality.mjs
  • scripts/lib/ratspeak-overlay-apply-list.sh
  • scripts/update.sh
  • src/main/index.ts
  • src/main/ipc/reticulum-db-handlers.test.ts
  • src/main/ipc/reticulum-db-handlers.ts
  • src/main/reticulum-sidecar-manager.test.ts
  • src/main/reticulum-sidecar-manager.ts
  • src/main/reticulumSidecarIssueTracker.test.ts
  • src/main/reticulumSidecarIssueTracker.ts
  • src/renderer/App.tsx
  • src/renderer/components/ConnectionPanel.tsx
  • src/renderer/components/ReticulumSidecarIssueAlertsBlock.test.tsx
  • src/renderer/components/ReticulumSidecarIssueAlertsBlock.tsx
  • src/renderer/components/ReticulumStackPanel.test.tsx
  • src/renderer/components/ReticulumStackPanel.tsx
  • src/renderer/components/flasher/BluetoothConfig.tsx
  • src/renderer/components/flasher/RNodeFlasherSection.tsx
  • src/renderer/components/reticulum/ReticulumInterfacesPanel.tsx
  • src/renderer/hooks/meshcore/meshcoreConnSideEffects.test.ts
  • src/renderer/hooks/meshcore/meshcoreConnSideEffects.ts
  • src/renderer/hooks/useSendMessage.test.ts
  • src/renderer/hooks/useSendMessage.ts
  • src/renderer/lib/diagnostics/ReticulumDiagnosticEngine.test.ts
  • src/renderer/lib/diagnostics/ReticulumDiagnosticEngine.ts
  • src/renderer/lib/diagnostics/RoutingDiagnosticEngine.test.ts
  • src/renderer/lib/diagnostics/RoutingDiagnosticEngine.ts
  • src/renderer/lib/flasher/rnode.test.ts
  • src/renderer/lib/flasher/rnode.ts
  • src/renderer/lib/ingest/reticulumIngest.test.ts
  • src/renderer/lib/meshcore/meshcoreTcpInitBurst.test.ts
  • src/renderer/lib/meshcore/meshcoreTcpInitBurst.ts
  • src/renderer/lib/radio/BaseRadioProvider.ts
  • src/renderer/lib/radio/protocol-capabilities.test.ts
  • src/renderer/lib/reticulum/reticulumBleBondDesync.test.ts
  • src/renderer/lib/reticulum/reticulumBleBondDesync.ts
  • src/renderer/lib/reticulum/reticulumLocalInterfaceHealth.test.ts
  • src/renderer/lib/reticulum/reticulumLocalInterfaceHealth.ts
  • src/renderer/lib/reticulum/reticulumLxmfOutboundBackchannel.contract.test.ts
  • src/renderer/lib/reticulum/reticulumNobleBleYield.test.ts
  • src/renderer/lib/reticulum/reticulumNobleBleYield.ts
  • src/renderer/lib/reticulum/useReticulumNobleBleYieldWatcher.ts
  • src/renderer/lib/sessions/meshcoreSession.ts
  • src/renderer/runtime/useMeshcoreRuntime.reconnect.test.ts
  • src/renderer/runtime/useMeshcoreRuntime.ts
  • src/renderer/runtime/useMeshtasticRuntime.ts
  • src/renderer/runtime/useReticulumRuntime.reconnect-hardening.test.ts
  • src/renderer/runtime/useReticulumRuntime.ts

Comment thread src/main/reticulum-sidecar-manager.ts
Comment thread src/renderer/components/ReticulumSidecarIssueAlertsBlock.tsx
Comment thread src/renderer/lib/flasher/rnode.test.ts
Comment thread src/renderer/runtime/useReticulumRuntime.ts
Apply still-valid review fixes: watchdog stop guard, connect fresh-start
coalesce, SoftAP latch reset, Noble yield abort, and focused regression tests.
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.

MeshCore hop goblins

1 participant