diff --git a/main/assets/js/search-data.json b/main/assets/js/search-data.json
index f500eb0341..113848d68c 100644
--- a/main/assets/js/search-data.json
+++ b/main/assets/js/search-data.json
@@ -1303,7 +1303,7 @@
},"186": {
"doc": "Codebase",
"title": "Build System",
- "content": "Gradle Kotlin DSL . All build files use Kotlin DSL (.gradle.kts). Configuration: . | Version catalog: gradle/libs.versions.toml | Convention plugins: build-logic/convention/ | Settings: settings.gradle.kts | . Convention Plugins . Located in build-logic/convention/src/main/kotlin/. The full set is registered in build-logic/convention/build.gradle.kts; these are the ones a module build applies most often: . | Plugin | Purpose | . | meshtastic.kmp.feature | Standard feature module setup | . | meshtastic.kmp.library | Shared KMP library module | . | meshtastic.kmp.library.compose | KMP library that also ships Compose UI | . | meshtastic.kmp.jvm.android | Adds the jvmAndroidMain source set shared by the Android and desktop JVM targets | . | meshtastic.koin | Koin Annotations + K2 compiler plugin | . | meshtastic.kotlinx.serialization | Serialization plugin setup | . | meshtastic.android.room | Room KMP setup and schema location | . | meshtastic.android.screenshot | Compose Preview screenshot testing | . The rest cover the application and library variants, lint, detekt, spotless, Dokka, Kover, AboutLibraries, analytics, secrets, the docs tasks and the root aggregate — read the register(…) block rather than assuming a plugin does or does not exist. Build Variants (Android) . | Flavor | Description | . | google | Google Play distribution; includes proprietary APIs | . | fdroid | F-Droid distribution; FOSS-only dependencies | . Key Gradle Tasks . # Compile check of every KMP module for JVM and iosSimulatorArm64 (excludes :desktopApp) ./gradlew kmpSmokeCompile # Run all tests: allTests covers KMP modules, test covers Android/JVM-only modules; run both ./gradlew test allTests # Code quality ./gradlew spotlessCheck detekt # Android build ./gradlew assembleGoogleDebug assembleFdroidDebug # Desktop run ./gradlew :desktopApp:run # Desktop native installers for the current OS (DMG / MSI+EXE / DEB+RPM+AppImage) ./gradlew :desktopApp:packageReleaseDistributionForCurrentOS # API reference (Dokka HTML → build/dokka/html) ./gradlew :dokkaGeneratePublicationHtml . ",
+ "content": "Gradle Kotlin DSL . All build files use Kotlin DSL (.gradle.kts). Configuration: . | Version catalog: gradle/libs.versions.toml | Convention plugins: build-logic/convention/ | Settings: settings.gradle.kts | . Convention Plugins . Located in build-logic/convention/src/main/kotlin/. The full set is registered in build-logic/convention/build.gradle.kts; these are the ones a module build applies most often: . | Plugin | Purpose | . | meshtastic.kmp.feature | Standard feature module setup | . | meshtastic.kmp.library | Shared KMP library module | . | meshtastic.kmp.library.compose | KMP library that also ships Compose UI | . | meshtastic.kmp.jvm.android | Adds the jvmAndroidMain source set shared by the Android and desktop JVM targets | . | meshtastic.koin | Koin Annotations + K2 compiler plugin | . | meshtastic.kotlinx.serialization | Serialization plugin setup | . | meshtastic.android.room | Room KMP setup and schema location | . | meshtastic.android.screenshot | Compose Preview screenshot testing | . The rest cover the application and library variants, lint, detekt, spotless, Dokka, Kover, AboutLibraries, analytics, secrets, the docs tasks and the root aggregate — read the register(…) block rather than assuming a plugin does or does not exist. Build Variants (Android) . | Flavor | Description | . | google | Google Play distribution; includes proprietary APIs | . | fdroid | F-Droid distribution; FOSS-only dependencies | . Key Gradle Tasks . # Compile check of every KMP module for JVM and iosSimulatorArm64 (excludes :desktopApp) ./gradlew kmpSmokeCompile # Run all tests: allTests covers KMP modules, test covers Android/JVM-only modules; run both ./gradlew test allTests # Code quality ./gradlew spotlessCheck detekt detektTypeResolved # Android build ./gradlew assembleGoogleDebug assembleFdroidDebug # Desktop run ./gradlew :desktopApp:run # Desktop native installers for the current OS (DMG / MSI+EXE / DEB+RPM+AppImage) ./gradlew :desktopApp:packageReleaseDistributionForCurrentOS # API reference (Dokka HTML → build/dokka/html) ./gradlew :dokkaGeneratePublicationHtml . ",
"url": "/Meshtastic-Android/main/en/developer/codebase.html#build-system",
"relUrl": "/en/developer/codebase.html#build-system"
@@ -3627,7 +3627,7 @@
},"518": {
"doc": "Contributing",
"title": "Development Workflow",
- "content": ". | Fork the repository (external contributors) or create a branch (maintainers). | Implement your changes following the architecture guidelines. | Test locally: ./gradlew spotlessCheck detekt kmpSmokeCompile test allTests | Commit with clear, descriptive messages. | Push and open a Pull Request. | . ",
+ "content": ". | Fork the repository (external contributors) or create a branch (maintainers). | Implement your changes following the architecture guidelines. | Test locally: ./gradlew spotlessCheck detekt detektTypeResolved kmpSmokeCompile test allTests | Commit with clear, descriptive messages. | Push and open a Pull Request. | . ",
"url": "/Meshtastic-Android/main/en/developer/contributing.html#development-workflow",
"relUrl": "/en/developer/contributing.html#development-workflow"
@@ -3641,7 +3641,7 @@
},"520": {
"doc": "Contributing",
"title": "Pull Request Checklist",
- "content": "Before submitting: . | Code compiles on all targets: ./gradlew kmpSmokeCompile | All tests pass: ./gradlew allTests | Code style passes: ./gradlew spotlessCheck | Static analysis passes: ./gradlew detekt | New code has appropriate test coverage | No android.* imports in commonMain | Koin modules registered if new DI is added | Routes added to Routes.kt if new navigation is introduced | Documentation updated if user-facing behavior changes | . ",
+ "content": "Before submitting: . | Code compiles on all targets: ./gradlew kmpSmokeCompile | All tests pass: ./gradlew allTests | Code style passes: ./gradlew spotlessCheck | Static analysis passes: ./gradlew detekt detektTypeResolved | New code has appropriate test coverage | No android.* imports in commonMain | Koin modules registered if new DI is added | Routes added to Routes.kt if new navigation is introduced | Documentation updated if user-facing behavior changes | . ",
"url": "/Meshtastic-Android/main/en/developer/contributing.html#pull-request-checklist",
"relUrl": "/en/developer/contributing.html#pull-request-checklist"
@@ -3662,7 +3662,7 @@
},"523": {
"doc": "Contributing",
"title": "Verification",
- "content": "Full pre-merge verification: ./gradlew spotlessCheck detekt kmpSmokeCompile test allTests . For docs-specific changes, also run: ./gradlew generateDocsBundle validateDocsBundle . Prose in docs/en/ follows Section 11 of the Meshtastic Client Design Standards — see Documentation Style for what’s specific to this repository. ",
+ "content": "Full pre-merge verification: ./gradlew spotlessCheck detekt detektTypeResolved kmpSmokeCompile test allTests . For docs-specific changes, also run: ./gradlew generateDocsBundle validateDocsBundle . Prose in docs/en/ follows Section 11 of the Meshtastic Client Design Standards — see Documentation Style for what’s specific to this repository. ",
"url": "/Meshtastic-Android/main/en/developer/contributing.html#verification",
"relUrl": "/en/developer/contributing.html#verification"
@@ -7988,7 +7988,7 @@
},"1141": {
"doc": "Developer Guide",
"title": "Before You Open a PR",
- "content": "Things that trip up first-time contributors — check these before requesting review: . | Formatting passes — run ./gradlew spotlessApply to auto-format, then verify with spotlessCheck | Detekt passes — run ./gradlew detekt and fix all reported issues | All tests pass — run ./gradlew test allTests (both are needed: test covers Android-only modules, allTests covers KMP) | Screenshot tests pass — if you touched any Compose UI, run ./gradlew :screenshot-tests:validateDebugScreenshotTest and update reference images if needed | Protos are an external dependency — protobuf models come from the org.meshtastic:protobufs Maven artifact (pinned in gradle/libs.versions.toml); change protos upstream and bump the version, never edit generated code locally | Docs updated — if you changed user-visible UI, update the corresponding page under docs/en/user/ | Previews updated — if you changed UI composables, update the corresponding *Previews.kt file and the screenshot-test baselines | Branch naming — branches must start with feat/, fix/, chore/, docs/, build/, ci/, refactor/, test/, or deps/ | . ",
+ "content": "Things that trip up first-time contributors — check these before requesting review: . | Formatting passes — run ./gradlew spotlessApply to auto-format, then verify with spotlessCheck | Detekt passes — run ./gradlew detekt detektTypeResolved and fix all reported issues; the second task runs the rules that need the compile classpath | All tests pass — run ./gradlew test allTests (both are needed: test covers Android-only modules, allTests covers KMP) | Screenshot tests pass — if you touched any Compose UI, run ./gradlew :screenshot-tests:validateDebugScreenshotTest and update reference images if needed | Protos are an external dependency — protobuf models come from the org.meshtastic:protobufs Maven artifact (pinned in gradle/libs.versions.toml); change protos upstream and bump the version, never edit generated code locally | Docs updated — if you changed user-visible UI, update the corresponding page under docs/en/user/ | Previews updated — if you changed UI composables, update the corresponding *Previews.kt file and the screenshot-test baselines | Branch naming — branches must start with feat/, fix/, chore/, docs/, build/, ci/, refactor/, test/, or deps/ | . ",
"url": "/Meshtastic-Android/main/en/developer.html#before-you-open-a-pr",
"relUrl": "/en/developer.html#before-you-open-a-pr"
@@ -42022,7 +42022,7 @@
},"6003": {
"doc": "Testing",
"title": "Running tests",
- "content": "# All tests: allTests covers KMP modules, test covers Android/JVM-only modules; run both ./gradlew test allTests # Specific module ./gradlew :feature:docs:allTests # Code quality ./gradlew spotlessCheck detekt # Full verification ./gradlew spotlessCheck detekt kmpSmokeCompile test allTests . ",
+ "content": "# All tests: allTests covers KMP modules, test covers Android/JVM-only modules; run both ./gradlew test allTests # Specific module ./gradlew :feature:docs:allTests # Code quality ./gradlew spotlessCheck detekt detektTypeResolved # Full verification ./gradlew spotlessCheck detekt detektTypeResolved kmpSmokeCompile test allTests . ",
"url": "/Meshtastic-Android/main/en/developer/testing.html#running-tests",
"relUrl": "/en/developer/testing.html#running-tests"
diff --git a/main/en/developer.html b/main/en/developer.html
index 4c0de51fe9..45b81e8cc6 100644
--- a/main/en/developer.html
+++ b/main/en/developer.html
@@ -1 +1 @@
-
Technical documentation for contributing to the Meshtastic Android and Desktop app.
Before You Open a PR
Things that trip up first-time contributors — check these before requesting review:
Formatting passes — run ./gradlew spotlessApply to auto-format, then verify with spotlessCheck
Detekt passes — run ./gradlew detekt and fix all reported issues
All tests pass — run ./gradlew test allTests (both are needed: test covers Android-only modules, allTests covers KMP)
Screenshot tests pass — if you touched any Compose UI, run ./gradlew :screenshot-tests:validateDebugScreenshotTest and update reference images if needed
Protos are an external dependency — protobuf models come from the org.meshtastic:protobufs Maven artifact (pinned in gradle/libs.versions.toml); change protos upstream and bump the version, never edit generated code locally
Docs updated — if you changed user-visible UI, update the corresponding page under docs/en/user/
Previews updated — if you changed UI composables, update the corresponding *Previews.kt file and the screenshot-test baselines
Branch naming — branches must start with feat/, fix/, chore/, docs/, build/, ci/, refactor/, test/, or deps/
What’s New for Developers
September 2026 — Navigation & Deep Links — Deep links route through Navigation 3’s UriDeepLinkMatcher instead of a hand-rolled when block, and patterns are anchored, so an unmodelled trailing path no longer opens the family root.
September 2026 — Measurement & Formatting — NumberFormatter.format follows the OS locale; formatInvariant is the fixed-dot one for payloads another system parses.
September 2026 — Documentation Style — Section 11 of the Meshtastic design standards is now the style guide for docs/en/; this page keeps only the repository mechanics, the in-app renderer’s admonition form, and the prose rules the standards leave open.
August 2026 — Documentation Style — New page: the house style guide for docs/en/ prose, with rule IDs, a project word list, and the reasoning behind each convention.
August 2026 — Map tile sources are one shared catalogue in feature/map (MapTileCatalogue, RasterTileSpec), so both flavors draw the same raster base maps and overlays from one definition.
August 2026 — Both maps draw an imported feature’s own icon and drape a KMZ GroundOverlay image at its LatLonBox (rotation included) — MapLibre via an ImageSource quad, Google via GroundOverlayOptions (#3786).
August 2026 — Offline map-pack downloads are gated on offlineMapsSupported, since the MapLibre offline API compiles on Desktop but silently downloads nothing there.
August 2026 — New module feature/map-maplibre: the F-Droid flavor and Desktop now render every map surface (main map, node track, traceroute, discovery, inline mini-map) through maplibre-compose from one multiplatform module, and osmdroid is gone. The shared rules both renderers must agree on live in feature/map policy classes.
Technical documentation for contributing to the Meshtastic Android and Desktop app.
Before You Open a PR
Things that trip up first-time contributors — check these before requesting review:
Formatting passes — run ./gradlew spotlessApply to auto-format, then verify with spotlessCheck
Detekt passes — run ./gradlew detekt detektTypeResolved and fix all reported issues; the second task runs the rules that need the compile classpath
All tests pass — run ./gradlew test allTests (both are needed: test covers Android-only modules, allTests covers KMP)
Screenshot tests pass — if you touched any Compose UI, run ./gradlew :screenshot-tests:validateDebugScreenshotTest and update reference images if needed
Protos are an external dependency — protobuf models come from the org.meshtastic:protobufs Maven artifact (pinned in gradle/libs.versions.toml); change protos upstream and bump the version, never edit generated code locally
Docs updated — if you changed user-visible UI, update the corresponding page under docs/en/user/
Previews updated — if you changed UI composables, update the corresponding *Previews.kt file and the screenshot-test baselines
Branch naming — branches must start with feat/, fix/, chore/, docs/, build/, ci/, refactor/, test/, or deps/
What’s New for Developers
September 2026 — Navigation & Deep Links — Deep links route through Navigation 3’s UriDeepLinkMatcher instead of a hand-rolled when block, and patterns are anchored, so an unmodelled trailing path no longer opens the family root.
September 2026 — Measurement & Formatting — NumberFormatter.format follows the OS locale; formatInvariant is the fixed-dot one for payloads another system parses.
September 2026 — Documentation Style — Section 11 of the Meshtastic design standards is now the style guide for docs/en/; this page keeps only the repository mechanics, the in-app renderer’s admonition form, and the prose rules the standards leave open.
August 2026 — Documentation Style — New page: the house style guide for docs/en/ prose, with rule IDs, a project word list, and the reasoning behind each convention.
August 2026 — Map tile sources are one shared catalogue in feature/map (MapTileCatalogue, RasterTileSpec), so both flavors draw the same raster base maps and overlays from one definition.
August 2026 — Both maps draw an imported feature’s own icon and drape a KMZ GroundOverlay image at its LatLonBox (rotation included) — MapLibre via an ImageSource quad, Google via GroundOverlayOptions (#3786).
August 2026 — Offline map-pack downloads are gated on offlineMapsSupported, since the MapLibre offline API compiles on Desktop but silently downloads nothing there.
August 2026 — New module feature/map-maplibre: the F-Droid flavor and Desktop now render every map surface (main map, node track, traceroute, discovery, inline mini-map) through maplibre-compose from one multiplatform module, and osmdroid is gone. The shared rules both renderers must agree on live in feature/map policy classes.
Single-runner jobs in reusable-check.yml run on the pinned Ubuntu LTS x64 label with JDK 25 and Gradle caching. Two jobs use a matrix: test-shards splits into shard-core, shard-feature and shard-app, and build-desktop runs across macOS, Windows and Linux x64 and arm64, on the labels its own matrix lists. Flatpak verification is its own workflow, not a job here. The pinned Ubuntu LTS arm label and the container-backed ubuntu-slim runners carry the lightweight jobs. .skills/testing-ci/SKILL.md has the four-tier rule.