From 3961f7ec04b5872b6c143c8a9d7d4155cff80afc Mon Sep 17 00:00:00 2001 From: Matt Johnson Date: Sun, 16 Aug 2026 02:58:00 +0000 Subject: [PATCH] fix: honour town_anchors.enabled in alert anchor-resolution queries resolve_anchor() (notifications/formatters/_anchor.py) and _location_anchor() (env/fire_render.py) selected all town_anchors rows regardless of the enabled flag, so a disabled anchor could still be used in outbound alert text -- only the dashboard/curation routes are meant to see disabled rows. Add AND enabled = 1 to both queries. No-op today: all 186 live town_anchors rows are enabled=1. Matters the next time someone disables an anchor from the curation UI. Also updates the traffic_last/0003.json wzdx golden literal in test_incident_refactor.py and adds TestAnchorResolve:: test_disabled_anchor_excluded, which forces the Photon fallback to miss and asserts a disabled-only DB row is not selected. --- work/meshai/env/fire_render.py | 2 +- .../notifications/formatters/_anchor.py | 2 +- work/tests/test_incident_refactor.py | 35 ++++++++++++++++++- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/work/meshai/env/fire_render.py b/work/meshai/env/fire_render.py index 04028fb..3bd0f27 100644 --- a/work/meshai/env/fire_render.py +++ b/work/meshai/env/fire_render.py @@ -248,7 +248,7 @@ def _location_anchor(n: dict) -> str: try: from meshai.persistence import get_db rows = get_db().execute( - "SELECT name, lat, lon FROM town_anchors WHERE lat IS NOT NULL AND lon IS NOT NULL" + "SELECT name, lat, lon FROM town_anchors WHERE lat IS NOT NULL AND lon IS NOT NULL AND enabled = 1" ).fetchall() best = None best_d = float("inf") diff --git a/work/meshai/notifications/formatters/_anchor.py b/work/meshai/notifications/formatters/_anchor.py index 10960e1..d6fb7ee 100644 --- a/work/meshai/notifications/formatters/_anchor.py +++ b/work/meshai/notifications/formatters/_anchor.py @@ -96,7 +96,7 @@ def resolve_anchor( from meshai.persistence import get_db rows = get_db().execute( "SELECT name, lat, lon FROM town_anchors " - "WHERE lat IS NOT NULL AND lon IS NOT NULL" + "WHERE lat IS NOT NULL AND lon IS NOT NULL AND enabled = 1" ).fetchall() best = None best_d = float("inf") diff --git a/work/tests/test_incident_refactor.py b/work/tests/test_incident_refactor.py index 8f6d1ca..3bb82c2 100644 --- a/work/tests/test_incident_refactor.py +++ b/work/tests/test_incident_refactor.py @@ -188,7 +188,11 @@ class TestWorkZoneGolden: _GOLDEN = { "0002.json": "🚧 US-91, near Chubbuck: southbound, road construction, ends Aug 17", - "0003.json": "🚧 US-95, near Wilder: southbound, ends Jul 19", + # "wilder" was added to the town_anchors seed by the seed-list sync + # (Fix 2), so the DB-anchor step now wins over the live Photon + # geocode this golden was originally captured against; the DB row's + # coords round to 1 mi S instead of Photon's sub-mile "near". + "0003.json": "🚧 US-95, 1 mi S of Wilder: southbound, ends Jul 19", } # Captured from the deleted normalizer's normalize() + _n_to_canonical_workzone() @@ -434,6 +438,35 @@ class TestAnchorResolve: assert resolve_anchor(None, -116.2, max_mi=50.0) is None assert resolve_anchor(43.6, None, max_mi=50.0) is None + def test_disabled_anchor_excluded(self, monkeypatch): + """A disabled=0 town_anchors row must not be selected, even when it is + the closest row within max_mi — the enabled flag is a hard exclude.""" + import time as _time + from meshai.persistence import get_db + from meshai.notifications.formatters._anchor import resolve_anchor + from meshai import geo + + # Force the Photon fallback to a known miss so a non-None result can + # only come from the (wrongly-included) disabled DB row. + monkeypatch.setattr( + geo, "nearest_town", + lambda lat, lon, max_distance_mi=50.0: None, + ) + + conn = get_db() + # Clear seeded anchors so only our controlled (disabled) row exists. + conn.execute("DELETE FROM town_anchors") + conn.execute( + "INSERT INTO town_anchors(name, lat, lon, state, enabled, updated_at) " + "VALUES (?, ?, ?, ?, ?, ?)", + ("disabledville", -33.8688, 151.2093, "NSW", 0, _time.time()), # Sydney + ) + + # Event right next to the disabled row; Photon fallback is forced to + # miss → None confirms the DB step excluded the disabled row. + result = resolve_anchor(-33.870, 151.210, max_mi=50.0) + assert result is None + # ── 4. Schema conformance ────────────────────────────────────────────────────