A unit call-sign is not a place
Build & Deploy / Build & push images (push) Successful in 4m9s
Build & Deploy / Deploy to VM (push) Successful in 2m12s
Build & Deploy / Report a failed deploy (push) Skipped

"Post 1-2" reached the geocoder, resolved against its talkgroup anchor and
produced a confident pin in the right town for an event with no known
location — while sitting in the same incident's `units` list the whole time.
A plausible wrong pin is worse than no pin: nothing downstream can tell it
is wrong.

Extraction returns `location` and `units` from one pass, so a string in both
is a misclassification, not two facts. Drop it before the geocoder sees it.

Closes server-26#52.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-08-23 23:16:48 -04:00
co-authored by Claude Opus 5
parent 964343c819
commit cc038e6326
3 changed files with 67 additions and 2 deletions
@@ -282,6 +282,27 @@ def clean_location(value) -> Optional[str]:
return s
def location_is_unit(location, units) -> bool:
"""
True when a location label is really one of the incident's own unit
call-signs.
Extraction returns `location` and `units` from the same pass, so a string
appearing in both is a misclassification, not two facts. "Post 1-2" reached
the geocoder that way, resolved against its talkgroup anchor, and produced a
confident pin in the right town for an event that has no known location at
all — worse than no pin, because nothing downstream can tell it is wrong.
See server-26#52.
Public because `intelligence.py` applies it at extraction time, alongside
`clean_location`, so the string never reaches the geocoder.
"""
key = _place_key(location)
if not key:
return False
return any(key == _place_key(u) for u in (units or []))
def _place_key(value) -> str:
"""Case- and punctuation-blind key for comparing two location labels."""
return re.sub(r"[^a-z0-9]+", " ", str(value or "").lower()).strip()
+13 -1
View File
@@ -19,7 +19,7 @@ from app.internal import area_context
# Location validity is defined once, by the module that owns the incident's
# location/pin invariant. incident_correlator does not import this module, so
# this is not a cycle.
from app.internal.incident_correlator import clean_location
from app.internal.incident_correlator import clean_location, location_is_unit
_PROMPT_TEMPLATE = """You are analyzing a P25 public safety radio recording. The audio was transcribed by Whisper through a digital radio vocoder, which introduces errors. Each numbered transmission is a separate PTT press from a different radio.
@@ -252,6 +252,18 @@ async def extract_scenes(
location: Optional[str] = clean_location(scene.get("location"))
vehicles: list[str] = scene.get("vehicles") or []
units: list[str] = scene.get("units") or []
# A "location" that is also one of this scene's own units is a unit
# call-sign, not a place. Both lists come from the same extraction pass,
# so the disagreement is free to detect and the string must be dropped
# before it reaches the geocoder — anchored place verification will
# otherwise resolve "Post 1-2" to a confident, plausible, wrong pin in
# the right town. See server-26#52.
if location and location_is_unit(location, units):
logger.info(
f"Intelligence: dropping location {location!r} — it is one of "
f"this scene's units, not a place"
)
location = None
cleared_units: list[str] = scene.get("cleared_units") or []
# Every call carries a severity — it is the signal the correlator uses to
# decide whether a call is incident-worthy at all, so it must never be