mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-08-26 17:31:34 +00:00
fix(satpass): default feed_source to native; correct misleading config comments
Central is retired -- its NATS broker (nats://central.echo6.mesh:4222) no longer exists -- but SatpassConfig.feed_source still defaulted to "central", overriding the _SourcedFeed mixin default of "native". A fresh install that enabled satpass got a silently dead adapter. The dashboard reinforced it: Environment.tsx seeded feed_source 'central' and labelled the adapter "via Central". Also corrects comments that documented the opposite of the code: the adapter_config keys usgs_quake.global_mag_floor / regional_mag_floor were labelled "CENTRAL-PATH ONLY", but notifications/gating/quake.py reads them unconditionally in the native path. Following those comments would have led someone to delete live config keys. - config.py: SatpassConfig.feed_source "central" -> "native" + docstring - adapter_config/defaults.py: correct the two "CENTRAL-PATH ONLY" comments - Environment.tsx: seed 'native'; reword the satpass subtitle - tests: assert the native default; pin central explicitly where a test exercises the central path or the feed_source flip Suite: 2337 passed, 6 pre-existing failures (stale SCHEMA_VERSION x3, expired TLE fixtures x2, one order-dependent), no new failures. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e9153943bf
commit
2dddc1a67b
5 changed files with 26 additions and 21 deletions
|
|
@ -66,7 +66,7 @@ const SATPASS_NATIVE_DEFAULT: EnvConfig['satpass'] = {
|
||||||
window_hours: 24,
|
window_hours: 24,
|
||||||
tle_refresh_seconds: 21600,
|
tle_refresh_seconds: 21600,
|
||||||
broadcast_lead_seconds: 3600,
|
broadcast_lead_seconds: 3600,
|
||||||
feed_source: 'central',
|
feed_source: 'native',
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sane defaults for the ipaws block so a GET payload predating the IPAWS
|
// Sane defaults for the ipaws block so a GET payload predating the IPAWS
|
||||||
|
|
@ -304,7 +304,7 @@ const META: Record<AdapterKey, AdapterMeta> = {
|
||||||
usgs_quake: { label: 'USGS Earthquakes', subtitle: 'Seismic events from the USGS feed', health: 'usgs_quake', hasCentral: true, nativeOnly: false, hasKey: true },
|
usgs_quake: { label: 'USGS Earthquakes', subtitle: 'Seismic events from the USGS feed', health: 'usgs_quake', hasCentral: true, nativeOnly: false, hasKey: true },
|
||||||
usgs: { label: 'USGS Stream Gauges', subtitle: 'River and stream water levels', health: 'usgs', hasCentral: true, nativeOnly: false, hasKey: true },
|
usgs: { label: 'USGS Stream Gauges', subtitle: 'River and stream water levels', health: 'usgs', hasCentral: true, nativeOnly: false, hasKey: true },
|
||||||
avalanche: { label: 'Avalanche Advisories', subtitle: 'Backcountry avalanche danger ratings', health: 'avalanche', hasCentral: true, nativeOnly: false, hasKey: true },
|
avalanche: { label: 'Avalanche Advisories', subtitle: 'Backcountry avalanche danger ratings', health: 'avalanche', hasCentral: true, nativeOnly: false, hasKey: true },
|
||||||
satpass: { label: 'Satellite Passes', subtitle: 'Observer pass alerts via Central', health: 'satpass', hasCentral: true, nativeOnly: false, hasKey: true },
|
satpass: { label: 'Satellite Passes', subtitle: 'Observer pass alerts from native TLE tracking', health: 'satpass', hasCentral: true, nativeOnly: false, hasKey: true },
|
||||||
ipaws: { label: 'FEMA IPAWS civil alerts', subtitle: 'Evacuations, AMBER, HazMat, 911 outages (non-weather)', health: 'ipaws', hasCentral: false, nativeOnly: true, hasKey: false },
|
ipaws: { label: 'FEMA IPAWS civil alerts', subtitle: 'Evacuations, AMBER, HazMat, 911 outages (non-weather)', health: 'ipaws', hasCentral: false, nativeOnly: true, hasKey: false },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -105,12 +105,12 @@ REGISTRY: dict[tuple[str, str], dict[str, Any]] = {
|
||||||
("usgs_quake", "global_mag_floor"): {
|
("usgs_quake", "global_mag_floor"): {
|
||||||
"default": 3.0, # quake_handler.py:69
|
"default": 3.0, # quake_handler.py:69
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"description": "Global magnitude floor for unconditional broadcasts. CENTRAL-PATH ONLY (central/quake_handler.py); the native feed_source gates on environmental.usgs_quake.min_magnitude instead.",
|
"description": "Global magnitude floor for unconditional broadcasts. Gates the shared native gating decider (meshai.notifications.gating.quake.decide), read regardless of feed_source.",
|
||||||
},
|
},
|
||||||
("usgs_quake", "regional_mag_floor"): {
|
("usgs_quake", "regional_mag_floor"): {
|
||||||
"default": 2.5, # quake_handler.py:70
|
"default": 2.5, # quake_handler.py:70
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"description": "Reduced magnitude floor for quakes within regional_radius_mi of centroid. CENTRAL-PATH ONLY (central/quake_handler.py); the native feed_source gates on environmental.usgs_quake.min_magnitude instead.",
|
"description": "Reduced magnitude floor for quakes within regional_radius_mi of centroid. Gates the shared native gating decider (meshai.notifications.gating.quake.decide), read regardless of feed_source.",
|
||||||
},
|
},
|
||||||
("usgs_quake", "escalate_mag_floor"): {
|
("usgs_quake", "escalate_mag_floor"): {
|
||||||
"default": 5.0, # quake_handler.py:76
|
"default": 5.0, # quake_handler.py:76
|
||||||
|
|
|
||||||
|
|
@ -444,8 +444,10 @@ class USGSQuakeConfig(_SourcedFeed):
|
||||||
# Native-path broadcast magnitude floor: the native adapter
|
# Native-path broadcast magnitude floor: the native adapter
|
||||||
# (env/usgs_quake.py) gates on this. THIS is the GUI-editable quake
|
# (env/usgs_quake.py) gates on this. THIS is the GUI-editable quake
|
||||||
# magnitude floor for the native feed_source. (The adapter_config
|
# magnitude floor for the native feed_source. (The adapter_config
|
||||||
# REGISTRY keys usgs_quake.global_mag_floor / regional_mag_floor apply
|
# REGISTRY keys usgs_quake.global_mag_floor / regional_mag_floor are
|
||||||
# only to the Central-firehose path in central/quake_handler.py.)
|
# separate: they gate the shared gating decider,
|
||||||
|
# meshai.notifications.gating.quake.decide(), which both the native
|
||||||
|
# path and central/quake_handler.py delegate to.)
|
||||||
min_magnitude: float = 2.5
|
min_magnitude: float = 2.5
|
||||||
# [west, south, east, north] -- Magic Valley -> Borah Peak -> Yellowstone
|
# [west, south, east, north] -- Magic Valley -> Borah Peak -> Yellowstone
|
||||||
bbox: list = field(default_factory=lambda: [-115.5, 42.0, -110.0, 45.2])
|
bbox: list = field(default_factory=lambda: [-115.5, 42.0, -110.0, 45.2])
|
||||||
|
|
@ -522,15 +524,15 @@ class FIRMSConfig(_SourcedFeed):
|
||||||
class SatpassConfig(_SourcedFeed):
|
class SatpassConfig(_SourcedFeed):
|
||||||
"""Satellite pass prediction settings.
|
"""Satellite pass prediction settings.
|
||||||
|
|
||||||
Historically a Central-only feed (`feed_source="central"`). The native
|
Native (`feed_source="native"`) is the default and only supported path:
|
||||||
path (`feed_source="native"`) adds a Celestrak TLE fetcher
|
a Celestrak TLE fetcher (`env.tle_fetch`) plus a native SGP4 predictor;
|
||||||
(`env.tle_fetch`) and a native SGP4 predictor; the fields below feed
|
the fields below feed those. Central is RETIRED — its NATS broker
|
||||||
those. `feed_source` default stays "central" — the flip to "native"
|
(`nats://central.echo6.mesh:4222`) no longer exists — so this no longer
|
||||||
happens at cutover, not here.
|
overrides the `_SourcedFeed` mixin default.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
enabled: bool = False
|
enabled: bool = False
|
||||||
feed_source: str = "central"
|
feed_source: str = "native"
|
||||||
|
|
||||||
# -- native path (TLE fetch + SGP4 predictor) -----------------------------
|
# -- native path (TLE fetch + SGP4 predictor) -----------------------------
|
||||||
# Ground stations the predictor computes passes for; each entry is a
|
# Ground stations the predictor computes passes for; each entry is a
|
||||||
|
|
|
||||||
|
|
@ -114,9 +114,12 @@ def test_feed_source_flip_to_central_requires_restart_and_does_not_swap():
|
||||||
|
|
||||||
|
|
||||||
def test_feed_source_flip_from_central_to_native_requires_restart():
|
def test_feed_source_flip_from_central_to_native_requires_restart():
|
||||||
# satpass defaults feed_source="central"; flip it to native.
|
# satpass now defaults to feed_source="native", so pin the starting state
|
||||||
store = EnvironmentalStore(_cfg())
|
# to "central" explicitly -- this exercises the flip, not the default.
|
||||||
assert "satpass" not in store._adapters # central by default -> no native instance
|
base_cfg = _cfg()
|
||||||
|
base_cfg.satpass = dataclasses.replace(base_cfg.satpass, feed_source="central")
|
||||||
|
store = EnvironmentalStore(base_cfg)
|
||||||
|
assert "satpass" not in store._adapters # central -> no native instance
|
||||||
|
|
||||||
new_cfg = _cfg()
|
new_cfg = _cfg()
|
||||||
new_cfg.satpass = dataclasses.replace(
|
new_cfg.satpass = dataclasses.replace(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"""Tests for satpass adapter registration on EnvironmentalConfig.
|
"""Tests for satpass adapter registration on EnvironmentalConfig.
|
||||||
|
|
||||||
Verifies:
|
Verifies:
|
||||||
- SatpassConfig exists and defaults to feed_source="central"
|
- SatpassConfig exists and defaults to feed_source="native"
|
||||||
- EnvironmentalConfig.satpass field is present and correctly typed
|
- EnvironmentalConfig.satpass field is present and correctly typed
|
||||||
- _subject_owned() includes central.sat.* subjects when satpass is registered
|
- _subject_owned() includes central.sat.* subjects when satpass is registered
|
||||||
- adapter_config REGISTRY contains satpass keys with valid types
|
- adapter_config REGISTRY contains satpass keys with valid types
|
||||||
|
|
@ -15,7 +15,7 @@ import pytest
|
||||||
def test_satpass_config_exists():
|
def test_satpass_config_exists():
|
||||||
from meshai.config import SatpassConfig
|
from meshai.config import SatpassConfig
|
||||||
cfg = SatpassConfig()
|
cfg = SatpassConfig()
|
||||||
assert cfg.feed_source == "central"
|
assert cfg.feed_source == "native"
|
||||||
assert cfg.enabled is False
|
assert cfg.enabled is False
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -37,10 +37,10 @@ def test_environmental_config_has_satpass():
|
||||||
assert isinstance(env.satpass, SatpassConfig)
|
assert isinstance(env.satpass, SatpassConfig)
|
||||||
|
|
||||||
|
|
||||||
def test_environmental_satpass_default_central():
|
def test_environmental_satpass_default_native():
|
||||||
from meshai.config import EnvironmentalConfig
|
from meshai.config import EnvironmentalConfig
|
||||||
env = EnvironmentalConfig()
|
env = EnvironmentalConfig()
|
||||||
assert env.satpass.feed_source == "central"
|
assert env.satpass.feed_source == "native"
|
||||||
|
|
||||||
|
|
||||||
# -- adapter_config REGISTRY --------------------------------------------------
|
# -- adapter_config REGISTRY --------------------------------------------------
|
||||||
|
|
@ -87,10 +87,10 @@ def test_yaml_parsing_satpass():
|
||||||
from meshai.config import SatpassConfig, _dict_to_dataclass, EnvironmentalConfig
|
from meshai.config import SatpassConfig, _dict_to_dataclass, EnvironmentalConfig
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
yaml_str = "environmental:\n satpass:\n enabled: true\n feed_source: central\n"
|
yaml_str = "environmental:\n satpass:\n enabled: true\n feed_source: native\n"
|
||||||
data = yaml.safe_load(yaml_str)
|
data = yaml.safe_load(yaml_str)
|
||||||
env = _dict_to_dataclass(EnvironmentalConfig, data["environmental"])
|
env = _dict_to_dataclass(EnvironmentalConfig, data["environmental"])
|
||||||
assert isinstance(env.satpass, SatpassConfig)
|
assert isinstance(env.satpass, SatpassConfig)
|
||||||
assert env.satpass.enabled is True
|
assert env.satpass.enabled is True
|
||||||
assert env.satpass.feed_source == "central"
|
assert env.satpass.feed_source == "native"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue