Skip to content

fix(analytics): cover local firmware sideloads and report message_send in the foreground - #6660

Merged
jamesarich merged 2 commits into
meshtastic:mainfrom
jamesarich:claude/analytics-rum-action-followup
Aug 12, 2026
Merged

fix(analytics): cover local firmware sideloads and report message_send in the foreground#6660
jamesarich merged 2 commits into
meshtastic:mainfrom
jamesarich:claude/analytics-rum-action-followup

Conversation

@jamesarich

@jamesarich jamesarich commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Why

Follow-up to #6654, which merged before its review feedback was applied. Two gaps in the named-RUM-action coverage that shipped:

  1. Local firmware sideloads are invisible. firmware_update_start is only emitted from startUpdate(). Picking a firmware file from disk goes through confirmLocalFirmwareFile()startUpdateFromFile(), a completely separate entry point that emits nothing. Firmware-update counts in RUM currently undercount, showing release-channel updates only. (Caught by CodeRabbit on feat(analytics): report key user interactions as named RUM actions #6654.)

  2. message_send fires outside the user's session. Text sends are not transmitted synchronously — SendMessageUseCase enqueues, and a WorkManager SendMessageWorker performs the actual radioController.sendMessage. Emitting the action there means it lands on a background thread, potentially long after the RUM session ended, and re-fires whenever the worker retries.

🐛 Fixes

Cover both firmware entry points. Extracted trackUpdateStart(state, releaseId) and call it from startUpdate() and startUpdateFromFile(). The local path reports the existing LOCAL_RELEASE_ID ("local") as release_version, so file-based flashes stay distinguishable from release updates. The call sits after the BLE address-validation guard, so a rejected start is not counted.

Report message_send where the user acted. Moved to SendMessageUseCase.invoke. Coverage is unchanged — every text-send path (messaging UI, notification quick-reply, Android Auto, AI function-calling) routes through this use case. Removed the TEXT_MESSAGE_APP branch from MessagingControllerImpl so messages are not counted twice. waypoint_send stays in the controller, since BaseMapViewModel calls radioController.sendMessage directly and never touches the use case.

🧹 Tests

The firmware fixtures passed an anonymous autofilled mock for the new constructor parameter, so they could pass without ever observing trackAction. Named the mock in all four fixtures and added assertions:

  • startUpdate reports firmware_update_start with the release version
  • confirmLocalFirmwareFile reports it with release_version=local — the newly covered path
  • an invalid BLE address reports no action — guards the early-return
  • SendMessageUseCase.invoke reports message_send

Testing Performed

spotlessCheck detekt assembleDebug :core:repository:allTests :core:service:allTests :feature:firmware:allTests on this branch's base — BUILD SUCCESSFUL in 2m 24s (Gradle exit 0), with all six analytics assertions confirmed executing and passing rather than replayed from cache.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Improvements
    • Message-send activity is now tracked consistently, including message size and reply status.
    • Duplicate text-message tracking has been prevented.
    • Firmware update tracking now includes transport type, recovery status, release version, and local-file updates.
    • Invalid firmware update attempts no longer generate misleading tracking events.
  • Tests
    • Added coverage for message and firmware update activity tracking across supported update methods.

…d in the foreground

CodeRabbit review of meshtastic#6654:
- confirmLocalFirmwareFile -> startUpdateFromFile is a second entry point
  for starting a flash and never emitted firmware_update_start. Both paths
  now go through a shared trackUpdateStart helper; the local path reports
  release_version=local.
- Name the analytics mocks in the firmware fixtures and assert the action,
  so the suites can no longer pass without observing it.

Also move message_send from MessagingControllerImpl to SendMessageUseCase.
Text sends are enqueued and transmitted by a WorkManager worker, so the
action was firing off the user's RUM session (possibly after it expired)
and re-firing on retry. Every text-send path routes through the use case,
so coverage is unchanged. Waypoints stay in the controller, which is where
the map's direct sendMessage call lands.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a7723f75-1f5e-41ec-98e8-3be17cc12b34

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The change adds analytics tracking for sent messages and firmware update starts. It injects PlatformAnalytics into affected components, prevents duplicate text-message events, records transport and release metadata, and adds validation tests.

Changes

Analytics event tracking

Layer / File(s) Summary
Message send analytics
core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/di/CoreRepositoryModule.kt, core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/usecase/SendMessageUseCase.kt, core/repository/src/commonTest/kotlin/org/meshtastic/core/repository/usecase/SendMessageUseCaseTest.kt
SendMessageUseCaseImpl receives PlatformAnalytics and records message_send with message byte length and reply status. Tests verify broadcast-message tracking.
Duplicate message tracking prevention
core/service/src/commonMain/kotlin/org/meshtastic/core/service/MessagingControllerImpl.kt
Text messages are excluded from the controller’s tracking path. Waypoint and generic data events remain tracked.
Firmware update analytics
feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModel.kt, feature/firmware/src/commonTest/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateIntegrationTest.kt, feature/firmware/src/commonTest/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModelTest.kt, feature/firmware/src/jvmTest/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModelFileTest.kt
Firmware-start tracking is centralized with transport, recovery, and release metadata. Tests cover BLE, USB local-file, valid, and invalid-address flows.

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

Sequence Diagram(s)

sequenceDiagram
  participant MessagingControllerImpl
  participant SendMessageUseCaseImpl
  participant PlatformAnalytics

  MessagingControllerImpl->>SendMessageUseCaseImpl: Send text message
  SendMessageUseCaseImpl->>PlatformAnalytics: Track message_send with byte length and reply status
Loading

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: jeremiah-k

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Tests Prove The Path, Not The End State ⚠️ Warning The new startUpdate assertion matches the parent implementation, and the invalid-BLE test passes with all local analytics removed; both tests can pass after the covered change is reverted. Remove the redundant startUpdate test and combine the valid and invalid local-file cases, or add a positive call-count assertion so the rejected-path test fails when local tracking is absent.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main changes: local firmware sideload analytics and foreground message_send tracking.
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.
Sibling Call Sites And Presence Semantics ✅ Passed The diff only adds analytics wiring/actions and moves tracking; it changes no nullable sensor field, zero-guard, or physical-value default, so sibling-call-site checking is not applicable.

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.

@github-actions github-actions Bot added the bugfix PR tag label Aug 12, 2026
@jamesarich
jamesarich enabled auto-merge August 12, 2026 19:58
@jamesarich

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@jamesarich
jamesarich added this pull request to the merge queue Aug 12, 2026

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

Actionable comments posted: 4

🤖 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
`@core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/usecase/SendMessageUseCase.kt`:
- Around line 135-140: Move the analytics.trackAction call out of the try/catch
covering savePacket and enqueue in SendMessageUseCase, then execute it with
safeCatching {} while preserving coroutine cancellation and logging failures as
warnings. Ensure analytics exceptions never alter the successful send result or
trigger the enqueue retry path.
- Line 139: Update the num_bytes value in SendMessageUseCase’s analytics map to
use finalMessageText.encodeToByteArray().size instead of character count,
matching DataPacket byte storage and MessagingControllerImpl reporting. Add a
test covering non-ASCII message text to verify the UTF-8 byte count.

In
`@core/repository/src/commonTest/kotlin/org/meshtastic/core/repository/usecase/SendMessageUseCaseTest.kt`:
- Around line 83-95: Update the analytics assertion in `invoke reports a
message_send analytics action` to use Mokkery verification with `exactly(1)`,
ensuring the `message_send` action is emitted once while preserving the existing
arguments.

In
`@feature/firmware/src/jvmTest/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModelFileTest.kt`:
- Around line 222-235: Strengthen the test `confirmLocalFirmwareFile with BLE
and invalid address reports no analytics action` by asserting the ViewModel
reaches `FirmwareUpdateState.Error` after confirmation and verifying
`firmwareUpdateManager.startUpdate` is never called. Keep the existing zero-call
analytics assertion so the test proves BLE validation rejected the update rather
than an earlier setup failure or early return.
🪄 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: CHILL

Plan: Pro Plus

Run ID: 069b7735-d3a7-4cf1-b2ac-5380a1b46b8c

📥 Commits

Reviewing files that changed from the base of the PR and between bc7634b and 8149c0a.

📒 Files selected for processing (8)
  • core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/di/CoreRepositoryModule.kt
  • core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/usecase/SendMessageUseCase.kt
  • core/repository/src/commonTest/kotlin/org/meshtastic/core/repository/usecase/SendMessageUseCaseTest.kt
  • core/service/src/commonMain/kotlin/org/meshtastic/core/service/MessagingControllerImpl.kt
  • feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModel.kt
  • feature/firmware/src/commonTest/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateIntegrationTest.kt
  • feature/firmware/src/commonTest/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModelTest.kt
  • feature/firmware/src/jvmTest/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModelFileTest.kt

CodeRabbit review of meshtastic#6660:
- trackAction ran inside the try guarding savePacket/enqueue. A telemetry
  failure after a successful enqueue was logged as 'Failed to enqueue' and
  rethrown, so a caller retry could queue a second copy of the message. It
  now runs outside that boundary via safeCatching, which preserves
  cancellation, and logs a warning instead.
- num_bytes counted characters while MessagingControllerImpl reports
  bytes.size, so the same attribute meant two different things. Use the
  UTF-8 byte count, covered by a non-ASCII test.
- Mokkery's default verify mode is soft and passes on duplicate calls, so
  the assertion could not catch the double-fire this change prevents. Use
  exactly(1).
- The invalid-BLE-address test also passed if preparation bailed out
  earlier; assert the Error state and that startUpdate was never called.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Merged via the queue into meshtastic:main with commit 4f86b91 Aug 12, 2026
3 checks passed
@jamesarich
jamesarich deleted the claude/analytics-rum-action-followup branch August 12, 2026 20:38
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