Citywide/large systems (e.g. NYPD Citywide) never get a location anchor, so location_proximity never fires and duplicate incidents get created for the same event at nearby addresses
#159
Live evidence, prod, 2026-09-20 ~23:00 UTC, via /admin Correlation Debug: two incidents 106 seconds apart on the same talkgroup (New York City - NYPD Citywide 2 Patch, TG 8026, system 5426129d-378e-4186-b583-ad5306747aa5):
874ea52a "Shots Fired at 1108 Jackson Avenue" (22:58:42) — suspect: male black, black hoodie/jacket/sweatshirt, jeans, knee pads, red stripes on the bottom
f776ef6b "Shots Fired at Jackson Avenue" (23:00:34) — suspect: male Black, slim build, black jacket, black sweats, red stripes on the bottom of the [pants]
Same block, matching and unusually specific suspect description (red stripes on the bottom), 2 minutes apart — this reads as the same real event reported twice, filed as two separate incidents. Both calls have location_coords: null. The rules engine AND the Gemini tier both independently returned new for the second call; the LLM's own corr_llm_reasoning was "which is a distinctly different location from all active incidents" — comparing the strings "1108 Jackson Avenue" and "Jackson Avenue" as text, with no coordinates to check.
Root cause, traced through app/internal/area_context.py and place_verifier.py: resolve_anchor() geocodes the system's configured municipality into {center, radius_km}, but discards the result when radius_km > settings.area_anchor_max_radius_km ("an anchor wider than the max is not written at all" — deliberate, per the module's own docstring, to avoid a radius so wide it "would confirm anything inside it"). "New York City" geocodes to a viewport far wider than any reasonable area_anchor_max_radius_km, so this system permanently has no anchor. place_verifier.py's own docstring: "NO ANCHOR MEANS SKIP, NOT ACCEPT" — without an anchor, place verification (and the geocode that would produce location_coords) is skipped entirely, for every call, forever, on this talkgroup.
Consequence for incident_correlator.py: location_coords never populates for this system, so the location_proximity fit signal (_call_fits_incident path 2, and the disambiguation weight in _disambiguate) can never fire. Every substantive call on a citywide system is reduced to unit_overlap-or-nothing, and the LLM tiebreak is asked to judge location purely as free text, which it gets wrong on exactly the case that matters most (a general street reference vs. a specific number on that street). Pulled from the same debug dump: every incident on TG 8026 (NYPD Citywide) in the sample has location_coords: null; incidents on smaller single-municipality systems (e.g. Ossining - Police Dispatch, TG 9600) do get real coordinates ({"lat": 41.1467702, "lng": -73.8653459} on "320 Spring Street"). This is systemic, not a one-off geocoder miss.
This is the same corr_path distribution the correlation debug endpoint already surfaces platform-wide (this 80-incident sample: 78 new vs 28 fast/thin + 15 fast/disambig + 2 fast/single + 2 location, and median_calls_per_incident: 1) — a large fraction of "new" decisions on a big city system are calls that a human would recognize as the same event, but that have no coordinate to check against.
Citywide/regional systems are exactly the ones with the highest call volume and the most concurrent incidents on one talkgroup — the population location_proximity exists to disambiguate — and they are structurally the ones that can never have it.
Not the same bug as server-26#158 (srcaddr unused in the thin-call path) — that's under-attachment of context-free acks to an existing incident; this is over-splitting of two substantive, information-bearing calls that should have merged.
Possible directions, not prescribing one: a sub-municipality anchor (borough/precinct/sector, whichever the radio system's config already carries) narrow enough to pass area_anchor_max_radius_km; a per-call point geocode (no anchor needed to sanity-check, just to get location_coords for the proximity math) with a separate, size-appropriate sanity radius for large systems; or teaching the LLM prompt to actually diff address-with-number vs. address-without-number as a fuzzy same-location signal instead of free-text guessing blind.
Live evidence, prod, 2026-09-20 ~23:00 UTC, via /admin Correlation Debug: two incidents 106 seconds apart on the same talkgroup (`New York City - NYPD Citywide 2 Patch`, TG 8026, system `5426129d-378e-4186-b583-ad5306747aa5`):
- `874ea52a` "Shots Fired at 1108 Jackson Avenue" (22:58:42) — suspect: male black, black hoodie/jacket/sweatshirt, jeans, knee pads, **red stripes on the bottom**
- `f776ef6b` "Shots Fired at Jackson Avenue" (23:00:34) — suspect: male Black, slim build, black jacket, black sweats, **red stripes on the bottom of the [pants]**
Same block, matching and unusually specific suspect description (red stripes on the bottom), 2 minutes apart — this reads as the same real event reported twice, filed as two separate incidents. Both calls have `location_coords: null`. The rules engine AND the Gemini tier both independently returned `new` for the second call; the LLM's own `corr_llm_reasoning` was *"which is a distinctly different location from all active incidents"* — comparing the strings "1108 Jackson Avenue" and "Jackson Avenue" as text, with no coordinates to check.
Root cause, traced through `app/internal/area_context.py` and `place_verifier.py`: `resolve_anchor()` geocodes the system's configured municipality into `{center, radius_km}`, but discards the result when `radius_km > settings.area_anchor_max_radius_km` ("an anchor wider than the max is not written at all" — deliberate, per the module's own docstring, to avoid a radius so wide it "would confirm anything inside it"). "New York City" geocodes to a viewport far wider than any reasonable `area_anchor_max_radius_km`, so this system permanently has no anchor. `place_verifier.py`'s own docstring: "NO ANCHOR MEANS SKIP, NOT ACCEPT" — without an anchor, place verification (and the geocode that would produce `location_coords`) is skipped entirely, for every call, forever, on this talkgroup.
Consequence for `incident_correlator.py`: `location_coords` never populates for this system, so the `location_proximity` fit signal (`_call_fits_incident` path 2, and the disambiguation weight in `_disambiguate`) can never fire. Every substantive call on a citywide system is reduced to unit_overlap-or-nothing, and the LLM tiebreak is asked to judge location purely as free text, which it gets wrong on exactly the case that matters most (a general street reference vs. a specific number on that street). Pulled from the same debug dump: every incident on TG 8026 (NYPD Citywide) in the sample has `location_coords: null`; incidents on smaller single-municipality systems (e.g. `Ossining - Police Dispatch`, TG 9600) do get real coordinates (`{"lat": 41.1467702, "lng": -73.8653459}` on "320 Spring Street"). This is systemic, not a one-off geocoder miss.
This is the same `corr_path` distribution the correlation debug endpoint already surfaces platform-wide (this 80-incident sample: 78 `new` vs 28 `fast/thin` + 15 `fast/disambig` + 2 `fast/single` + 2 `location`, and `median_calls_per_incident: 1`) — a large fraction of "new" decisions on a big city system are calls that a human would recognize as the same event, but that have no coordinate to check against.
Citywide/regional systems are exactly the ones with the highest call volume and the most concurrent incidents on one talkgroup — the population location_proximity exists to disambiguate — and they are structurally the ones that can never have it.
Not the same bug as server-26#158 (srcaddr unused in the thin-call path) — that's under-attachment of context-free acks to an existing incident; this is over-splitting of two substantive, information-bearing calls that should have merged.
Possible directions, not prescribing one: a sub-municipality anchor (borough/precinct/sector, whichever the radio system's config already carries) narrow enough to pass `area_anchor_max_radius_km`; a per-call point geocode (no anchor needed to sanity-check, just to get `location_coords` for the proximity math) with a separate, size-appropriate sanity radius for large systems; or teaching the LLM prompt to actually diff address-with-number vs. address-without-number as a fuzzy same-location signal instead of free-text guessing blind.
Fixed in 66bbf5b, pushed to main and live in prod (verified via /health git_sha). 422 pass, 0 fail. Also manually merged the two duplicate prod incidents this bug produced ("Shots Fired at 1108 Jackson Avenue" call reattached to "Shots Fired at Jackson Avenue" via the Archive page's attach/detach; the emptied duplicate auto-resolved). Follow-up filed at #160 (same gap in place_verifier.py, not fixed here).
Fixed in 66bbf5b, pushed to main and live in prod (verified via /health git_sha). 422 pass, 0 fail. Also manually merged the two duplicate prod incidents this bug produced ("Shots Fired at 1108 Jackson Avenue" call reattached to "Shots Fired at Jackson Avenue" via the Archive page's attach/detach; the emptied duplicate auto-resolved). Follow-up filed at #160 (same gap in place_verifier.py, not fixed here).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Live evidence, prod, 2026-09-20 ~23:00 UTC, via /admin Correlation Debug: two incidents 106 seconds apart on the same talkgroup (
New York City - NYPD Citywide 2 Patch, TG 8026, system5426129d-378e-4186-b583-ad5306747aa5):874ea52a"Shots Fired at 1108 Jackson Avenue" (22:58:42) — suspect: male black, black hoodie/jacket/sweatshirt, jeans, knee pads, red stripes on the bottomf776ef6b"Shots Fired at Jackson Avenue" (23:00:34) — suspect: male Black, slim build, black jacket, black sweats, red stripes on the bottom of the [pants]Same block, matching and unusually specific suspect description (red stripes on the bottom), 2 minutes apart — this reads as the same real event reported twice, filed as two separate incidents. Both calls have
location_coords: null. The rules engine AND the Gemini tier both independently returnednewfor the second call; the LLM's owncorr_llm_reasoningwas "which is a distinctly different location from all active incidents" — comparing the strings "1108 Jackson Avenue" and "Jackson Avenue" as text, with no coordinates to check.Root cause, traced through
app/internal/area_context.pyandplace_verifier.py:resolve_anchor()geocodes the system's configured municipality into{center, radius_km}, but discards the result whenradius_km > settings.area_anchor_max_radius_km("an anchor wider than the max is not written at all" — deliberate, per the module's own docstring, to avoid a radius so wide it "would confirm anything inside it"). "New York City" geocodes to a viewport far wider than any reasonablearea_anchor_max_radius_km, so this system permanently has no anchor.place_verifier.py's own docstring: "NO ANCHOR MEANS SKIP, NOT ACCEPT" — without an anchor, place verification (and the geocode that would producelocation_coords) is skipped entirely, for every call, forever, on this talkgroup.Consequence for
incident_correlator.py:location_coordsnever populates for this system, so thelocation_proximityfit signal (_call_fits_incidentpath 2, and the disambiguation weight in_disambiguate) can never fire. Every substantive call on a citywide system is reduced to unit_overlap-or-nothing, and the LLM tiebreak is asked to judge location purely as free text, which it gets wrong on exactly the case that matters most (a general street reference vs. a specific number on that street). Pulled from the same debug dump: every incident on TG 8026 (NYPD Citywide) in the sample haslocation_coords: null; incidents on smaller single-municipality systems (e.g.Ossining - Police Dispatch, TG 9600) do get real coordinates ({"lat": 41.1467702, "lng": -73.8653459}on "320 Spring Street"). This is systemic, not a one-off geocoder miss.This is the same
corr_pathdistribution the correlation debug endpoint already surfaces platform-wide (this 80-incident sample: 78newvs 28fast/thin+ 15fast/disambig+ 2fast/single+ 2location, andmedian_calls_per_incident: 1) — a large fraction of "new" decisions on a big city system are calls that a human would recognize as the same event, but that have no coordinate to check against.Citywide/regional systems are exactly the ones with the highest call volume and the most concurrent incidents on one talkgroup — the population location_proximity exists to disambiguate — and they are structurally the ones that can never have it.
Not the same bug as server-26#158 (srcaddr unused in the thin-call path) — that's under-attachment of context-free acks to an existing incident; this is over-splitting of two substantive, information-bearing calls that should have merged.
Possible directions, not prescribing one: a sub-municipality anchor (borough/precinct/sector, whichever the radio system's config already carries) narrow enough to pass
area_anchor_max_radius_km; a per-call point geocode (no anchor needed to sanity-check, just to getlocation_coordsfor the proximity math) with a separate, size-appropriate sanity radius for large systems; or teaching the LLM prompt to actually diff address-with-number vs. address-without-number as a fuzzy same-location signal instead of free-text guessing blind.Fixed in
66bbf5b, pushed to main and live in prod (verified via /health git_sha). 422 pass, 0 fail. Also manually merged the two duplicate prod incidents this bug produced ("Shots Fired at 1108 Jackson Avenue" call reattached to "Shots Fired at Jackson Avenue" via the Archive page's attach/detach; the emptied duplicate auto-resolved). Follow-up filed at #160 (same gap in place_verifier.py, not fixed here).