|
1 | 1 | # Feature Specification: Local Mesh Discovery |
2 | 2 |
|
3 | | -**Feature Branch**: `001-local-mesh-discovery` |
| 3 | +**Feature Branch**: `feat/discovery` |
4 | 4 | **Created**: 2026-05-07 |
5 | | -**Status**: Not Started |
6 | | -**Input**: User description: "Local Mesh Discovery — a high-fidelity diagnostic and community-mapping tool that cycles through modem presets to audit the local RF environment" |
| 5 | +**Updated**: 2026-05-18 |
| 6 | +**Status**: Implementation Complete (pending final verification D048) |
| 7 | +**Input**: User description: "Local Mesh Discovery — a high-fidelity diagnostic and community-mapping tool that cycles through modem presets to audit the local RF environment" |
| 8 | +**Cross-Platform Pair**: `meshtastic/Meshtastic-Apple:specs/001-local-mesh-discovery/` (Status: ✅ Merged to main) |
7 | 9 |
|
8 | 10 | ## Summary |
9 | 11 |
|
@@ -359,3 +361,114 @@ If two presets still tie after all heuristics, the UI labels them as tied and av |
359 | 361 | - `core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/Routes.kt` |
360 | 362 | - `core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/DeepLinkRouter.kt` |
361 | 363 | - `core/database/src/commonMain/kotlin/org/meshtastic/core/database/MeshtasticDatabase.kt` |
| 364 | + |
| 365 | +--- |
| 366 | + |
| 367 | +## Implementation Status (2026-05-18) |
| 368 | + |
| 369 | +### User Story Completion |
| 370 | + |
| 371 | +| User Story | Status | Notes | |
| 372 | +|---|---|---| |
| 373 | +| US1 — Multi-Preset Scan | ✅ Complete | Full state machine, reconnect, dwell, advancement | |
| 374 | +| US2 — Map Visualization | ✅ Complete | CompositionLocal map, preset filter, topology overlay, direct/mesh color-coding | |
| 375 | +| US3 — Summary + AI | ✅ Complete (AI fallback only) | Deterministic 6-level ranking, per-preset AI summaries field, Gemini Nano provider stubbed (delegates to algorithmic) | |
| 376 | +| US4 — Persistence & History | ✅ Complete | Room KMP, cascade delete, history list, detail view | |
| 377 | +| US5 — 2.4 GHz Gating | ⚠️ Logic only | `Check24GhzCapability` implemented + tested; not wired to PresetPickerCard UI gates | |
| 378 | +| Export/Share | ⚠️ Partial | `PdfDiscoveryExporter` + `TextDiscoveryExporter` implemented; UI hookup pending | |
| 379 | + |
| 380 | +### Implementation Divergences from Original Spec |
| 381 | + |
| 382 | +The implementation evolved beyond the original spec in several areas. This section documents the actual state: |
| 383 | + |
| 384 | +#### Data Model — Simplified Entity Structure |
| 385 | + |
| 386 | +The actual Room entities use a simpler schema than `data-model.md` proposed: |
| 387 | + |
| 388 | +- **`DiscoverySessionEntity`** uses auto-generated `Long` PK (not String UUID), fewer fields, and includes `userLatitude`/`userLongitude` (not in original spec). |
| 389 | +- **`DiscoveryPresetResultEntity`** uses `presetName: String` (not `presetKey` + `presetIndex`), and adds full RF health fields: `numPacketsTx`, `numPacketsRx`, `numPacketsRxBad`, `numRxDupe`, `numTxRelay`, `numTxRelayCanceled`, `numOnlineNodes`, `numTotalNodes`, `uptimeSeconds`, `avgChannelUtilization`, `avgAirtimeRate`, `packetSuccessRate`, `packetFailureRate`, `aiSummary`. |
| 390 | +- **`DiscoveredNodeEntity`** adds `neighborType: String` ("direct"/"mesh") and `messageCount`/`sensorPacketCount` — not in original spec but aligning with Apple implementation. |
| 391 | +- A unified `DiscoveryDao` serves all queries (rather than 3 separate DAOs as proposed). |
| 392 | + |
| 393 | +#### RF Health & LocalStats — Fully Implemented |
| 394 | + |
| 395 | +The implementation captures full `LocalStats` proto fields per-preset (Apple FR-008/FR-012/FR-024 equivalent): |
| 396 | +- `numPacketsTx`, `numPacketsRx`, `numPacketsRxBad`, `numRxDupe` |
| 397 | +- `packetSuccessRate`, `packetFailureRate` |
| 398 | +- `avgChannelUtilization` (from `DeviceMetrics.channel_utilization`) |
| 399 | +- `avgAirtimeRate` (from delta `air_util_tx` via 2-Packet Rule) |
| 400 | + |
| 401 | +UI: `RfHealthSection.kt` renders these in the preset result cards. |
| 402 | + |
| 403 | +#### Direct vs. Mesh Node Classification — Implemented |
| 404 | + |
| 405 | +Nodes are classified as `"direct"` (seen via their own packets) or `"mesh"` (discovered only through `NeighborInfo` from another node). Map visualization uses `DiscoveryNeighborType.DIRECT`/`MESH` for color differentiation — aligning with Apple's green/blue color-coding. |
| 406 | + |
| 407 | +#### Per-Preset AI Summaries — Field Present |
| 408 | + |
| 409 | +`DiscoveryPresetResultEntity.aiSummary` stores per-preset summaries (Apple FR-021 equivalent). The summary generator populates these with algorithmic descriptions; the field is ready for Gemini Nano output when integrated. |
| 410 | + |
| 411 | +#### State Machine Implementation Names |
| 412 | + |
| 413 | +| Spec Name | Implementation Name | Notes | |
| 414 | +|---|---|---| |
| 415 | +| WaitingForReconnect | Reconnecting | Semantic equivalent | |
| 416 | +| SwitchingPreset | Shifting | Matches "Shifting to [preset]" UX text | |
| 417 | +| Completed (terminal) | Complete | Differentiated by `completionStatus` on session entity | |
| 418 | + |
| 419 | +--- |
| 420 | + |
| 421 | +## Cross-Platform Alignment with Meshtastic-Apple |
| 422 | + |
| 423 | +The Apple implementation (`meshtastic/Meshtastic-Apple`) is merged to `main` and provides the cross-platform reference. This section documents alignment and intentional differences. |
| 424 | + |
| 425 | +### Fully Aligned Areas |
| 426 | + |
| 427 | +| Feature | Android | Apple | Status | |
| 428 | +|---|---|---|---| |
| 429 | +| Core scan concept | Cycle presets → dwell → collect → summarize | Same | ✅ Aligned | |
| 430 | +| Entity triad | Session / PresetResult / DiscoveredNode | Same | ✅ Aligned | |
| 431 | +| Minimum dwell | 15 minutes | 15 minutes | ✅ Aligned | |
| 432 | +| 2.4 GHz gating approach | DeviceHardwareRepository tag check | DeviceHardwareEntity tags | ✅ Aligned | |
| 433 | +| Home preset snapshot + restore | Before first switch, restore on end | Same | ✅ Aligned | |
| 434 | +| NeighborInfo pipeline reuse | Existing handler | Same | ✅ Aligned | |
| 435 | +| BLE reconnect reuse | BleReconnectPolicy | Existing BLE actor | ✅ Aligned | |
| 436 | +| Deep link slug | `localMeshDiscovery` | `localMeshDiscovery` | ✅ Aligned | |
| 437 | +| RF Health metrics | All LocalStats fields | Same | ✅ Aligned | |
| 438 | +| Direct/mesh node classification | `neighborType` field | Same | ✅ Aligned | |
| 439 | +| User position on session | `userLatitude`/`userLongitude` | Same | ✅ Aligned | |
| 440 | +| Channel utilization + airtime | 2-Packet Rule computation | Same | ✅ Aligned | |
| 441 | +| Per-preset AI summary field | `aiSummary` on PresetResult | Same | ✅ Aligned | |
| 442 | +| Export | PDF primary, text fallback | PDF via UIGraphicsPDFRenderer | ✅ Aligned | |
| 443 | + |
| 444 | +### Intentional Differences (Android Advantages) |
| 445 | + |
| 446 | +| Feature | Android | Apple | Rationale | |
| 447 | +|---|---|---|---| |
| 448 | +| Navigation location | Settings > Advanced (production) | Settings > Developers (DEBUG only) | Android treats this as a power-user feature, not debug-only | |
| 449 | +| Two-level state machine | Session + Preset-level states | Single-level | Better partial-session tracking, per-preset SKIPPED state | |
| 450 | +| `isPartial` flag | Explicit bool on session | `completionStatus` string only | Clearer query semantics | |
| 451 | +| `medianSnr` | On PresetResult | Not stored | Richer ranking input | |
| 452 | +| `reconnectCount` | Per-preset | Not tracked | Useful for reliability analysis | |
| 453 | +| `actualDwellSeconds` | Separate from planned | Not stored | Shows reconnect-time loss | |
| 454 | +| KMP + Desktop | Full commonMain logic + JVM Desktop shell | iOS-only | Architectural requirement | |
| 455 | +| `bestPresetKey` + `recommendationSource` | Stored on session | Computed at render time | Faster history list rendering | |
| 456 | + |
| 457 | +### Known Divergences (Potential Future Alignment) |
| 458 | + |
| 459 | +| Feature | Apple Has | Android Status | Priority | |
| 460 | +|---|---|---|---| |
| 461 | +| Radar sweep animation | `RadarSweepView` at 60fps | Not planned | 🟡 Low — cosmetic, high battery cost | |
| 462 | +| Node social/sensor icon classification | `person.2.fill` vs `thermometer` | Data available (`messageCount`/`sensorPacketCount`) but no icon rule defined | 🟡 Medium — could add | |
| 463 | +| Map auto-zoom (1.6×, 0.005° min, 0.8s ease) | Specified | Uses platform map default auto-fit | 🟡 Low — platform maps handle this differently | |
| 464 | +| Dwell picker specific values | `[1, 5, 15, 30, 45, 60, 90, 120, 180]` min | Slider with 15-min minimum | 🟡 Low — UX preference | |
| 465 | +| Historical sessions fed to AI | Trend/cross-session analysis | Session-level only currently | 🟡 Medium — future enhancement | |
| 466 | +| Reconnect timeout default | 60 seconds explicit | Configurable, no spec'd default | 🟢 Low — uses BleReconnectPolicy defaults | |
| 467 | + |
| 468 | +### Design Repo Status |
| 469 | + |
| 470 | +The `meshtastic/design` repo (`standards/audits/cross-platform-spec-audit.md`) confirms: |
| 471 | +- Android: 50/51 tasks complete on `feat/discovery` — remaining: D048 full verification |
| 472 | +- Apple: ✅ Implemented on main |
| 473 | +- No feature-level design spec exists (design repo is visual standards only) |
| 474 | +- Design standard color palette (Success green `#3FB86D`, Info blue `#5C6BC0`) should be used for direct/mesh node map colors |
0 commit comments