Files

5.5 KiB

Implementation Plan: Core Model (Domain Models)

Branch: 017-core-model | Date: 2026-07-27 | Spec: spec.md Input: Feature specification from /specs/017-core-model/spec.md Status: Migrated — all implementation complete, plan reverse-engineered from existing code.

Summary

Core Model is a pure domain layer with 57 commonMain files (~4,500 LOC) defining all domain types, utility functions, and extensions. It has no external dependencies beyond proto definitions and Okio. The module is the most stable in the codebase — changes are infrequent and typically additive (new fields, new utilities).

Technical Context

Language/Version: Kotlin 2.3+ targeting JDK 21
Primary Dependencies: Wire protobuf (core/proto), Okio (ByteString), kotlinx.serialization (ByteStringSerializer)
Testing: 6 commonTest + 3 androidDeviceTest files, ~600 LOC
Target Platform: Android, Desktop (JVM), iOS
Constraints: Pure domain — no DI, no coroutines, no persistence, no network
Scale/Scope: 57 commonMain files (~4,500 LOC), 5 platform files (~200 LOC), 9 test files (~600 LOC)

Constitution Check

Principle Status Notes
I. Kotlin Multiplatform Core PASS 57 of 57 domain files in commonMain. Platform actuals limited to DateTime + Random.
II. Zero Lint Tolerance PASS detekt-baseline.xml present. @Suppress("MagicNumber") on Node.
VII. Coroutine Safety N/A No coroutines in this module.
IX. Branch & Scope Hygiene PASS Module scoped to org.meshtastic.core.model.

Gate Result: All applicable principles satisfied.

Project Structure

core/model/src/
├── commonMain/kotlin/org/meshtastic/core/model/
│   ├── Node.kt (231 LOC)           # Primary domain model
│   ├── DataPacket.kt                # Mesh packet representation
│   ├── Message.kt                   # Chat message model
│   ├── Contact.kt                   # Contact representation
│   ├── Channel.kt                   # Channel config model
│   ├── DeviceVersion.kt             # Firmware version parser
│   ├── Capabilities.kt              # Feature capability flags
│   ├── ConnectionState.kt           # Connection state enum
│   ├── SessionStatus.kt             # Remote admin session status
│   ├── NodeSortOption.kt            # Sort options
│   ├── RadioController.kt           # Radio control interface
│   ├── ... (15+ more domain types)
│   ├── service/
│   │   ├── ServiceAction.kt         # Service action sealed class
│   │   └── TracerouteResponse.kt    # Traceroute result model
│   └── util/
│       ├── ChannelSet.kt            # URL encode/decode
│       ├── MeshDataMapper.kt        # Proto → domain mapping
│       ├── TimeUtils.kt             # Time formatting
│       ├── Extensions.kt            # General extensions
│       ├── ... (15+ more utilities)
├── commonTest/ (6 files)
├── androidDeviceTest/ (3 files)
├── jvmAndroidMain/ (2 files — DateTime, Random)
├── androidMain/ (2 files — Uri, TimeZone)
└── iosMain/ (1 file — noop)

Implementation Phases

Phase 1 — Domain Models (Complete)

Core domain types: Node, DataPacket, Message, Contact, Channel, ConnectionState, DeviceVersion, Capabilities, SessionStatus, plus ~15 supporting types.

Phase 2 — Utilities & Extensions (Complete)

25+ utility files: time/date formatting, distance/coordinate calculations, URL encoding, proto extensions, byte string helpers, hashing, random generation.

Technical Decisions

Decision Choice Rationale
Node model Single data class with 25+ properties Aggregates all node-related data for convenient access
isOnline Computed property vs lastHeard threshold Simple, predictable, no caching needed
Colors num-based deterministic generation Consistent per-node colors without needing a color assignment system
Position validation latitude_i != 0 && longitude_i != 0 Matches firmware convention; (0,0) treated as "no position"
Channel URL Base64url without padding Safe for URL embedding, matches Meshtastic web client
Version parsing Regex-based DeviceVersion Handles major.minor.patch.hash and major.minor.patch formats
Platform actuals DateTime + Random only Minimal surface; everything else is pure Kotlin

Gaps Identified

Gap Severity Recommendation
Node.distance() has no unit test ⚠️ Medium Add test with known coordinate pairs
Node.bearing() has no unit test ⚠️ Medium Add test with cardinal direction verification
Node.colors has no test ⚠️ Low Verify contrast ratio for light/dark backgrounds
DataPacket has no test ⚠️ Low Test nodeNumToDefaultId conversion
Message has no test ⚠️ Low Test equality and display formatting
Channel has only androidDeviceTest ⚠️ Low Consider migrating to commonTest
ChannelSet has only androidDeviceTest ⚠️ Medium URL round-trip test should be in commonTest for cross-platform validation
MeshDataMapper has no test ⚠️ Medium Add tests for proto → domain mapping correctness
TimeUtils has no test ⚠️ Low Add formatting boundary tests
DistanceExtensions has no test ⚠️ Low Add metric ↔ imperial conversion tests