Stop ambient radio chatter from opening incidents, and refill the map
The 23:46Z correlation dump confirmed the severity gate fixed the problem it
was written for -- orphans fell from 69 to 16, and only three of those are
after the deploy boundary, two of them deliberate skips. Nothing on TG 9048
absorbs the channel any more; the largest post-deploy incident is four calls
over nine minutes and is genuinely one event.
It overcorrected. 37 of 50 incidents were open, most a single routine call.
The cause was the gate's own substance test, which counted `units` and
`location`. Radio protocol puts a unit ID in essentially every transmission
and a place name in most of them, so has_substance was true almost always and
the severity check never actually ran -- "11-Victor, 72 at Holland Station"
became its own permanent incident. Substance is now a vehicle, a geocode or a
tag: things the extractor found beyond who was speaking and where they stood.
Severity still opens an incident on its own, so nothing real is lost.
incident_type is now validated against the enum the prompt offers rather than
trusted. It is written straight through to incident.type and rendered as the
title, so a model that answered the severity question in the type field
produced an incident titled "Routine -- TGID 9563". Unrecognised values become
None and fall to the tag/severity path, which is what "unknown" already did.
The map was empty for a separate reason: geocoding accepted only ROOFTOP and
RANGE_INTERPOLATED. Dispatch names places the way people speak, and Google
returns GEOMETRIC_CENTER for exactly those forms -- intersections ("Lake
Street and Veterans Memorial Drive") and named POIs ("Brewster Station").
Requiring a street address discarded nearly every real dispatch location and
left only numbered addresses plotted, which is why the July incidents have
coordinates and none since do. GEOMETRIC_CENTER is now accepted; APPROXIMATE
is still rejected, since a region centroid is what an ungeocodable string
degrades to. Note this is necessary but may not be sufficient -- if
GOOGLE_MAPS_API_KEY is unset on the host the map stays empty regardless, and
that has not been checked from here.
Two things found and deliberately not fixed, both in DEFERRED.md. One call can
still land in two incidents, because upload.py correlates each extracted scene
independently and the model over-split one conversation; multi-scene is
intentional, so that is prompt tuning rather than a code change. And nothing
closes an incident that merely goes quiet -- signal-resolution and master
auto-resolve both exist, but a one-call incident nobody clears stays active
forever. That wanted the over-creation fixed first so a time-based sweeper
would not just paper over it.
Gate tests updated: units and location alone must now orphan, and the case
that matters most is kept explicit -- units with a real severity still open an
incident. 17 pass. No new environment variables, so CI deploys this without an
ansible run.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
53965e1a19
commit
96625fabd0
@@ -67,10 +67,8 @@ def test_any_real_severity_opens_an_untyped_incident(severity):
|
||||
|
||||
|
||||
@pytest.mark.parametrize("field,value", [
|
||||
("call_units", ["6 Adam"]),
|
||||
("call_vehicles", ["RMP 22146"]),
|
||||
("coords", {"lat": 41.0, "lng": -73.8}),
|
||||
("location", "District 6"),
|
||||
("tags", ["prisoner-transport"]),
|
||||
])
|
||||
def test_concrete_content_opens_an_untyped_incident(field, value):
|
||||
@@ -80,6 +78,32 @@ def test_concrete_content_opens_an_untyped_incident(field, value):
|
||||
assert decision["incident_type"] == "other"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("field,value", [
|
||||
("call_units", ["11-Victor"]),
|
||||
("location", "Holland Station"),
|
||||
])
|
||||
def test_ambient_radio_fields_are_not_substance(field, value):
|
||||
"""
|
||||
A unit ID and a place name appear in nearly every transmission, so treating
|
||||
them as substance made the severity check dead code: "11-Victor, 72 at
|
||||
Holland Station" opened its own incident, and 37 of 50 incidents were single
|
||||
routine calls left permanently active.
|
||||
"""
|
||||
assert _run_decision(_ctx(**{field: value}))["action"] == "orphan"
|
||||
|
||||
|
||||
def test_units_and_location_together_still_orphan():
|
||||
decision = _run_decision(_ctx(call_units=["11-Victor"], location="Holland Station"))
|
||||
assert decision["action"] == "orphan"
|
||||
|
||||
|
||||
def test_units_with_real_severity_still_open_an_incident():
|
||||
"""Severity is the gate — ambient fields don't block it, they just can't open it alone."""
|
||||
decision = _run_decision(_ctx(call_units=["11-Victor"], call_severity="moderate"))
|
||||
assert decision["action"] == "new"
|
||||
assert decision["incident_type"] == "other"
|
||||
|
||||
|
||||
def test_explicit_type_is_never_downgraded_to_other():
|
||||
decision = _run_decision(_ctx(incident_type="police", call_severity="moderate"))
|
||||
assert decision["action"] == "new"
|
||||
|
||||
Reference in New Issue
Block a user