- Extended RegionAnchor with local_name, description, aliases, cities
- Moved region geographic context from hardcoded Python to config.yaml
- Added 7-day stale node purge in _do_refresh (556 → 267 nodes)
- Fixed coverage lookup: str(node_num) → node_num (int key)
- Added bidirectional neighbor lookup for better region assignment
- Dynamic geography building in router from config
- Reporter reads region context from config instead of hardcoded dict
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
NodeHealth is gone. MeshHealth.nodes is now dict[int, UnifiedNode].
Reporter reads all fields from UnifiedNode: coverage, environment,
neighbors, hw_model — everything available without cross-referencing.
This eliminates the entire category of field missing on NodeHealth bugs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update HealthScore with coverage pillar (20% weight)
- Adjust weights: Infra 30%, Util 25%, Coverage 20%, Behavior 15%, Power 10%
- Add coverage metrics: avg_gateways, single_gw_count, full_count
- Add health score fields to UnifiedNode for direct sync
- compute() now syncs scores back to UnifiedNode objects
- Coverage scoring penalizes single-gateway nodes
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Root cause: health engine keyed nodes by database row IDs instead of
Meshtastic node numbers. Packets and telemetry could never match.
Fixed:
- Store _node_num on all normalized nodes (mesh_sources.py)
- Key health engine node dict by _node_num (mesh_health.py)
- Fix packet field names: from_node not from/fromId
- Fix telemetry parsing: handle telemetryType/value structure
- Increase packet/telemetry fetch limits for 24h coverage
- Fix utilization formula to compute actual airtime percentage
- router.py: Fixed region scope detection to match longest region name first
- router.py: Added region abbreviations (SCID, SWID, etc.) for quick matching
- router.py: Added city name mapping (Boise -> South Western ID, etc.)
- router.py: Fixed node longname matching (case-insensitive substring)
- router.py: Added follow-up message context tracking (_user_mesh_context)
- router.py: Added more mesh keywords (noisy, traffic, packets, etc.)
- mesh_reporter.py: Added disambiguation for duplicate shortnames in region detail
- mesh_health.py: Added util_data_available flag to track packet data presence
- mesh_health.py: Passes has_packet_data through score computation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New files:
- mesh_reporter.py: MeshReporter class for prompt injection
- build_tier1_summary(): ~500-800 token mesh health summary
- build_region_detail(): Detailed region breakdown
- build_node_detail(): Single node info with recommendations
- build_recommendations(): Optimization suggestions
- build_lora_compact(): Short format for LoRa messages
- list_regions_compact(): Region list with scores
- commands/health.py: !health and !region commands
- !health: Quick mesh summary (no LLM)
- !region [name]: Region info or list all regions
Modified files:
- router.py: Mesh question detection and prompt injection
- _is_mesh_question(): Keyword/phrase matching
- _detect_mesh_scope(): Node/region/mesh scope detection
- Inject Tier 1/2 data for mesh questions
- Add mesh awareness instructions to LLM
- main.py: Create MeshReporter, pass to dispatcher/router
- commands/dispatcher.py: Register health/region commands
- mesh_health.py: Fix role type (int -> str)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Nodes without GPS can be assigned to a region based on their
neighbors. Uses edge data to build neighbor graph, then assigns
unlocated nodes to the most common region among their neighbors.
Iterates until no more assignments possible.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Regions are now user-defined anchor points (name + lat/lon)
- Nodes assigned to nearest region, no distance limits
- Removed auto-naming and region_labels/infra_overrides
- Added Idaho region defaults in TUI
- Simpler, deterministic, user-controlled
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- last_lat/last_long are scaled integers (divide by 1e7)
- last_seen_us is in microseconds (divide by 1e6)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements mesh intelligence with geo clustering, four-pillar health scoring,
and auto-naming regions from GPS data.
New: geo.py, mesh_health.py
Modified: config.py, main.py, router.py, configurator.py, config.example.yaml
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>