From ad33e6e02bdd568cc9303de5f3a2def796b81c7f Mon Sep 17 00:00:00 2001 From: malice Date: Wed, 8 Jul 2026 13:09:06 -0600 Subject: [PATCH] feat(nws): register weather_watch/advisory with NWS formatter + cutover (#96) weather_watch and weather_advisory were falling through to the legacy Mode-B renderer (composer.py), which appended the raw expiry epoch (`exp 1783615500.0` from _context_segment) and overflowed 140 chars, splitting into two packets with the epoch stranded in packet 2. Fix: register both categories with the NWS formatter and gating decider (same path as weather_warning/weather_statement), and add them to MESHAI_CUTOVER_CATEGORIES in docker-compose.yml so the pipeline routes them to the new formatter on deploy. Co-authored-by: Matt Johnson Co-authored-by: Claude Sonnet 4.6 --- work/docker-compose.yml | 3 +++ work/meshai/notifications/formatters/__init__.py | 10 +++++++--- work/meshai/notifications/gating/__init__.py | 10 +++++++--- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/work/docker-compose.yml b/work/docker-compose.yml index 6d06d77..ffcead4 100644 --- a/work/docker-compose.yml +++ b/work/docker-compose.yml @@ -55,6 +55,9 @@ services: environment: # API key can be set here or in config.yaml - LLM_API_KEY=${LLM_API_KEY:-} + # Staged cutover: categories fully migrated to the new formatter+decider + # pipeline. Append new categories here as they clear shadow bake. + - MESHAI_CUTOVER_CATEGORIES=earthquake_event,geomagnetic_storm,rf_propagation_alert,avalanche_warning,avalanche_watch,weather_warning,weather_statement,weather_watch,weather_advisory,work_zone,road_incident,road_closure,traffic_congestion,stream_flow,wildfire_declared,wildfire_incident,wildfire_closed # Limit resources deploy: diff --git a/work/meshai/notifications/formatters/__init__.py b/work/meshai/notifications/formatters/__init__.py index ce904dc..75810ab 100644 --- a/work/meshai/notifications/formatters/__init__.py +++ b/work/meshai/notifications/formatters/__init__.py @@ -73,11 +73,15 @@ from meshai.notifications.formatters import swpc as _swpc_fmt_mod # noqa: E402, register("geomagnetic_storm", _swpc_fmt_mod.format) register("rf_propagation_alert", _swpc_fmt_mod.format) -# Phase-2: NWS weather alerts (weather_warning + weather_statement). -# weather_watch and weather_advisory are not yet migrated (Phase-2 scope). +# Phase-2: NWS weather alerts (weather_warning + weather_statement + +# weather_watch + weather_advisory). All four categories share the same +# NWS formatter; category controls the event-type string / emoji, not +# the render path. from meshai.notifications.formatters import nws as _nws_fmt_mod # noqa: E402,F401 -register("weather_warning", _nws_fmt_mod.format) +register("weather_warning", _nws_fmt_mod.format) register("weather_statement", _nws_fmt_mod.format) +register("weather_watch", _nws_fmt_mod.format) +register("weather_advisory", _nws_fmt_mod.format) # Phase-2: incident / roads categories. # One formatter handles all four; event.category drives the render path. diff --git a/work/meshai/notifications/gating/__init__.py b/work/meshai/notifications/gating/__init__.py index 65602c8..4c938fc 100644 --- a/work/meshai/notifications/gating/__init__.py +++ b/work/meshai/notifications/gating/__init__.py @@ -64,11 +64,15 @@ from meshai.notifications.gating import swpc as _swpc_gate_mod # noqa: E402,F40 register("geomagnetic_storm", _swpc_gate_mod.decide) register("rf_propagation_alert", _swpc_gate_mod.decide) -# Phase-2: NWS weather alerts (weather_warning + weather_statement). -# weather_watch and weather_advisory are not yet migrated (Phase-2 scope). +# Phase-2: NWS weather alerts (weather_warning + weather_statement + +# weather_watch + weather_advisory). All four categories share the same +# NWS gating decider; the severity override in _severity_override_for() +# correctly fires only for _warning categories. from meshai.notifications.gating import nws as _nws_gate_mod # noqa: E402,F401 -register("weather_warning", _nws_gate_mod.decide) +register("weather_warning", _nws_gate_mod.decide) register("weather_statement", _nws_gate_mod.decide) +register("weather_watch", _nws_gate_mod.decide) +register("weather_advisory", _nws_gate_mod.decide) # Phase-2: incident / roads categories. # One decider handles all four; it reads external_id to decide dedup path.