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 <mj@k7zvx.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
malice 2026-07-08 13:09:06 -06:00 committed by GitHub
commit ad33e6e02b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 17 additions and 6 deletions

View file

@ -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:

View file

@ -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.

View file

@ -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.