Skip to content

fix(connections): send set_time_only at MyNodeInfo instead of onNodeDbReady - #6503

Merged
jamesarich merged 2 commits into
mainfrom
claude/settime-config-handshake-0f58d1
Jul 28, 2026
Merged

fix(connections): send set_time_only at MyNodeInfo instead of onNodeDbReady#6503
jamesarich merged 2 commits into
mainfrom
claude/settime-config-handshake-0f58d1

Conversation

@jamesarich

@jamesarich jamesarich commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

🛠️ Why

The app has sent set_time_only on every config handshake since #4103 — but at the end of the handshake (onNodeDbReady). By then the firmware has already begun flushing its queued packet backlog to the phone, so the backlog is stamped with the device's old clock. Firmware meshtastic/firmware#11274 introduces backlog re-timing, which only helps if the phone's time arrives before the packets are requested.

What changed

  • set_time_only now goes out the moment the MyNodeInfo frame arrives — the first Stage 1 frame, and the earliest point the local node number is authoritative (new MeshConnectionManager.onMyNodeInfoReceived hook).
  • The send bypasses the outbound packet queue via a new CommandSender.sendAdminImmediate: the queue only drains while Connected, which would otherwise hold the packet until after the backlog flush (verified on hardware — the queued path delayed it ~4s, landing after the flush began).
  • The get_owner_request session-passkey seeding stays at onNodeDbReady, unchanged.

🧪 Testing Performed

  • New unit test pins the sendAdminImmediate contract: direct ToRadio dispatch (not the Connected-gated queue), destination, ADMIN_APP portnum, want_response=false, and session_passkey injection.
  • spotlessCheck detekt :core:data:allTests :core:repository:allTests :core:service:allTests :core:takserver:allTests all green.
  • E2E on Pixel 9 Pro (fdroid debug) + RAK4631 (WisMesh Pocket) over BLE, watching the firmware serial log:
    • Before: Client received set_time_only command arrived ~100 ms after the first backlog phone downloaded packet.
    • After: it arrives during the Stage 1 config stream (state=3), ~5 s before Config Send Complete and the backlog/replay drain. Handshake completes normally — no GATT contention from the single early write.
    • (Test node has GPS, so firmware correctly ignored the NTP-quality time — the ordering is what was under test.)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Improvements
    • Device time is now set earlier during connection setup, improving message timing and synchronization.
    • Local node information is processed immediately after handshake progress updates.
    • Administrative commands can be sent immediately when needed, bypassing the normal outbound packet queue.
  • Documentation
    • Expanded guidance on when local node number information is first authoritative during the connection stream.
  • Tests
    • Updated and added coverage for immediate admin message dispatch, including passkey injection and expected packet attributes.

…bReady

Sending device time at the end of the handshake stamps the firmware's
queued packet backlog with the old clock. Move the send to the moment
the MyNodeInfo frame arrives — the earliest point the local node number
is authoritative — so firmware with meshtastic/firmware#11274 can
re-time backlog packets before flushing them to the phone.

The send must bypass the outbound packet queue (new
CommandSender.sendAdminImmediate): the queue only drains while
Connected, which would hold the packet until after the backlog flush.
The get_owner_request passkey seeding stays at onNodeDbReady.

Verified on hardware (Pixel 9 Pro + RAK4631): set_time_only now arrives
during the Stage 1 config stream, ~5s before the backlog drain, vs
~100ms after the flush began previously.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the bugfix PR tag label Jul 28, 2026
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e4bb6190-49b1-437b-8d07-98bee51ffcdf

📥 Commits

Reviewing files that changed from the base of the PR and between e4a4c83 and 72edaa4.

📒 Files selected for processing (1)
  • core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/CommandSenderImplTest.kt

📝 Walkthrough

Walkthrough

The PR adds immediate admin packet dispatch and moves the device time update to the MyNodeInfo handshake event. NodeDB readiness continues with session passkey seeding, and test doubles implement the new interfaces.

Changes

Admin time synchronization

Layer / File(s) Summary
Immediate admin command path
core/repository/.../CommandSender.kt, core/data/.../CommandSenderImpl.kt, core/data/.../CommandSenderImplTest.kt, core/data/.../LockdownCoordinatorImplTest.kt, core/takserver/.../TAKMeshIntegrationTest.kt
Adds and verifies sendAdminImmediate, which injects the session passkey, builds an admin packet, and sends it directly through packetHandler.
MyNodeInfo handshake timing
core/repository/.../MeshConnectionManager.kt, core/data/.../MeshConfigFlowManagerImpl.kt, core/data/.../MeshConnectionManagerImpl.kt, core/data/.../LockdownCoordinatorImplTest.kt
Publishes my_node_num when MyNodeInfo arrives, sends set_time_only immediately, removes the later time update from onNodeDbReady(), and updates the connection-manager test double.

Estimated code review effort: 3 (Moderate) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MeshConfigFlowManagerImpl
  participant MeshConnectionManagerImpl
  participant CommandSenderImpl
  participant packetHandler
  MeshConfigFlowManagerImpl->>MeshConnectionManagerImpl: onMyNodeInfoReceived(my_node_num)
  MeshConnectionManagerImpl->>CommandSenderImpl: sendAdminImmediate(set_time_only)
  CommandSenderImpl->>packetHandler: sendToRadio(ToRadio(packet))
  MeshConfigFlowManagerImpl->>MeshConnectionManagerImpl: onNodeDbReady()
  MeshConnectionManagerImpl->>CommandSenderImpl: sendAdmin(get_owner_request)
Loading
🚥 Pre-merge checks | ✅ 4
✅ 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 matches the main change: moving set_time_only from onNodeDbReady to MyNodeInfo handling.
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.

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
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/CommandSenderImpl.kt (1)

185-189: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add regression coverage for the immediate-dispatch contract.

The implementation looks correct, but the supplied test changes only add no-op sendAdminImmediate stubs. Add a test that verifies direct ToRadio dispatch, session_passkey injection, and want_response = false, so a future refactor cannot silently route this command back through the queue.

Based on the supplied test changes, the new fake methods do not assert this behavior.

🤖 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
`@core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/CommandSenderImpl.kt`
around lines 185 - 189, Extend the tests for sendAdminImmediate to capture the
ToRadio sent directly by packetHandler.sendToRadio, assert the packet targets
the requested destination with want_response = false, and verify the admin
message receives the passkey from sessionManager.getPasskey. Replace no-op fake
sendAdminImmediate methods with assertions or add coverage around
CommandSenderImpl.sendAdminImmediate so direct dispatch and session_passkey
injection remain enforced.
🤖 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.

Nitpick comments:
In
`@core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/CommandSenderImpl.kt`:
- Around line 185-189: Extend the tests for sendAdminImmediate to capture the
ToRadio sent directly by packetHandler.sendToRadio, assert the packet targets
the requested destination with want_response = false, and verify the admin
message receives the passkey from sessionManager.getPasskey. Replace no-op fake
sendAdminImmediate methods with assertions or add coverage around
CommandSenderImpl.sendAdminImmediate so direct dispatch and session_passkey
injection remain enforced.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 32757138-b80e-4582-b379-f4cf65dbb919

📥 Commits

Reviewing files that changed from the base of the PR and between 3913939 and e4a4c83.

📒 Files selected for processing (7)
  • core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/CommandSenderImpl.kt
  • core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshConfigFlowManagerImpl.kt
  • core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImpl.kt
  • core/data/src/commonTest/kotlin/org/meshtastic/core/data/manager/LockdownCoordinatorImplTest.kt
  • core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/CommandSender.kt
  • core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/MeshConnectionManager.kt
  • core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/TAKMeshIntegrationTest.kt

Asserts direct ToRadio dispatch (not the Connected-gated queue),
destination, ADMIN_APP portnum, no want_response, and session_passkey
injection, per review feedback on #6503.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jamesarich

Copy link
Copy Markdown
Collaborator Author

Added regression coverage in CommandSenderImplTest.sendAdminImmediate_dispatchesDirectToRadioWithPasskeyAndNoResponse — asserts direct ToRadio dispatch, destination, ADMIN_APP portnum, no want_response, and session_passkey injection (72edaa4).

🤖 Addressed by Claude Code

@jamesarich
jamesarich added this pull request to the merge queue Jul 28, 2026
Merged via the queue into main with commit 8a4e2e3 Jul 28, 2026
15 checks passed
@jamesarich
jamesarich deleted the claude/settime-config-handshake-0f58d1 branch July 28, 2026 23:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix PR tag

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant