correlator: give the LLM tier what it needs to link, stop it defaulting to "new" (server-26#115)

The 2026-09-07 measurement window (CORRELATION_REVIEW_0907.md) showed the
consensus tiebreaker was the dominant over-split driver: it ran on 45% of
calls and resolved link/orphan disagreements as "new" ~24/25 of the time,
shattering one Mohegan Park car-alarm job into 9 incidents and opening ~7
incidents from radio checks / roll calls.

Two causes, two fixes:

1. `_inc_summary` gave the model `id|type|loc|units|tags|idle` — no title,
   no talkgroup. It literally could not see that two "car alarms, Mohegan
   Park Ave/Avenue" incidents on TG 9560 were the same. Now includes the
   incident title (the strongest same-event signal) and talkgroup.

2. `_RULES` told the model "orphan when in doubt — conservative is always
   correct". For a system that over-splits, that is backwards: a wrong link
   is cheap, a duplicate incident is the failure. Rewritten to: prefer link
   for a plausible same-talkgroup continuation (low bar), reserve "new" for a
   genuinely different event, and explicitly "orphan" non-incidents (radio
   checks, roll call, 10-8/10-98, mileage logs).

Plus `_extract_road_ids` now canonicalises street-type synonyms
(Avenue→ave, Street→st, Road→rd, ...), so "Mohegan Park Avenue" and
"Mohegan Park Ave" share a road id — that one difference was splitting the
car-alarm incident.

+tests/test_correlator_115.py. Full c2-core suite green (sandboxed venv).
Bigger levers deferred to follow-ups: the consensus escalation itself (should
a cheap-LLM "orphan" ever reach a tiebreak?), a first-class road-overlap fit
signal in _call_fits_incident, geocode coverage.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-09-07 16:53:03 -04:00
co-authored by Claude Sonnet 5
parent 0712e7a437
commit 3a944f35c1
3 changed files with 103 additions and 10 deletions
+54
View File
@@ -0,0 +1,54 @@
"""
server-26#115 — the tiebreaker manufactured incidents because it was blind to
what would tell it two incidents are one.
Two low-risk supports for the reframed prompt:
1. `_extract_road_ids` collapses street-type synonyms, so "Mohegan Park Ave"
and "Mohegan Park Avenue" share a road id (they were splitting one
car-alarm incident into two).
2. `_inc_summary` now carries the incident title and talkgroup, the two
signals the model needs to recognise a same-channel continuation.
"""
from datetime import datetime, timezone
from app.internal.incident_correlator import (
_extract_road_ids, _location_mentions_road_overlap,
)
from app.internal.llm_correlator import _inc_summary
NOW = datetime(2026, 9, 7, 8, 0, 0, tzinfo=timezone.utc)
def test_avenue_and_ave_are_the_same_road_id():
assert _extract_road_ids("Mohegan Park Avenue") == _extract_road_ids("Mohegan Park Ave")
assert _extract_road_ids("191 Broadway Street") == _extract_road_ids("191 Broadway St")
assert _extract_road_ids("North State Road") == _extract_road_ids("North State Rd")
def test_road_overlap_matches_across_the_synonym():
assert _location_mentions_road_overlap("multiple car alarms Mohegan Park Avenue",
["patrol to Mohegan Park Ave"]) is True
# still discriminates genuinely different streets
assert _location_mentions_road_overlap("Oak Avenue", ["Elm Avenue"]) is False
def test_inc_summary_carries_title_and_talkgroup():
s = _inc_summary({
"incident_id": "abc123",
"type": "police",
"talkgroup_ids": [9560],
"title": "Nuisance Alarm at Mohegan Park Ave",
"location": "Mohegan Park Ave",
"units": ["Headquarters"],
"tags": ["car-alarm"],
"updated_at": NOW.isoformat(),
}, NOW)
assert "title:'Nuisance Alarm at Mohegan Park Ave'" in s
assert "tg:[9560]" in s
assert "id:abc123" in s
def test_inc_summary_omits_missing_optional_fields():
s = _inc_summary({"incident_id": "x", "updated_at": NOW.isoformat()}, NOW)
assert "title:" not in s and "tg:" not in s and "loc:" not in s
assert s.startswith("id:x")