correlator: remove is_dispatch and the tactical fit path entirely (#134) #135

Merged
logan merged 3 commits from fix/134-drop-tactical-fit-path into main 2026-09-13 14:55:04 -04:00
3 changed files with 23 additions and 7 deletions
Showing only changes of commit 0473e6a583 - Show all commits
+6 -4
View File
@@ -97,11 +97,13 @@ class Settings(BaseSettings):
# Across that dump every correct thin attach was <= 3.4 min idle and every wrong # Across that dump every correct thin attach was <= 3.4 min idle and every wrong
# one was >= 8.2, so 5 separates them with room on both sides. Genuine # one was >= 8.2, so 5 separates them with room on both sides. Genuine
# back-and-forth is handled by the 30-second tier-1 path above this. # back-and-forth is handled by the 30-second tier-1 path above this.
# Second consumer (server-26#115): routers/upload.py's LLM-orphan-gate escape # Second consumer (server-26#115): routers/upload.py's LLM-orphan-gate
# hatch (_recent_incident_on_same_talkgroup) reuses this same value, selected # escape hatch (_recent_incident_on_same_talkgroup) always uses this same
# the same way (dispatch vs tactical) via _is_dispatch_channel. Retuning this # value now — no dispatch/tactical branch there since #133. Retuning this
# for fast/thin reasons moves that gate's behavior too — check both call # for fast/thin reasons moves that gate's behavior too — check both call
# sites before changing it. # sites before changing it. (tg_thin_idle_minutes below is now unused in
# production — is_dispatch is hardcoded True at its only real call site,
# incident_correlator.py's fast-path — see server-26#134.)
tg_dispatch_thin_idle_minutes: int = 5 tg_dispatch_thin_idle_minutes: int = 5
# Every other channel: tier-2 thin calls attach to a lone candidate idle < this. # Every other channel: tier-2 thin calls attach to a lone candidate idle < this.
# Non-dispatch talkgroups previously had NO tier-2 bound at all — they used the # Non-dispatch talkgroups previously had NO tier-2 bound at all — they used the
@@ -976,7 +976,11 @@ def _run_decision(ctx: dict) -> dict:
# directly on the Firestore call doc). Fall back to the call doc so that # directly on the Firestore call doc). Fall back to the call doc so that
# dispatch-channel strictness works regardless of how the call arrived. # dispatch-channel strictness works regardless of how the call arrived.
effective_talkgroup_name = talkgroup_name or call_doc.get("talkgroup_name") effective_talkgroup_name = talkgroup_name or call_doc.get("talkgroup_name")
is_dispatch = _is_dispatch_channel(effective_talkgroup_name) # server-26#134: always dispatch-strict now, not name-guessed — a
# channel labeled "tac"/"tactical" is rare and no less scrutinized in
# practice than any other. _call_fits_incident's tactical branch is
# kept, unreached, in case that's ever wrong.
is_dispatch = True
if effective_talkgroup_name != talkgroup_name: if effective_talkgroup_name != talkgroup_name:
logger.info( logger.info(
f"Correlator: talkgroup_name missing from request for call {call_id}, " f"Correlator: talkgroup_name missing from request for call {call_id}, "
@@ -153,12 +153,22 @@ def test_thin_call_with_no_overlap_does_not_attach_on_a_tactical_channel():
assert decision["action"] == "orphan" assert decision["action"] == "orphan"
def test_tactical_thin_call_still_attaches_inside_its_own_window(): def test_tactical_named_channel_uses_the_dispatch_window_now():
"""Bounded, not removed — a "10-4" on a working channel is still context.""" """server-26#134: is_dispatch is hardcoded True — a channel's name no
longer changes the thin window. 14 min (the old tactical bound minus 1,
inside the old 15-min window) is now past the 5-min dispatch window."""
inc = _incident(idle_minutes=settings.tg_thin_idle_minutes - 1) inc = _incident(idle_minutes=settings.tg_thin_idle_minutes - 1)
decision = _run_decision(_ctx( decision = _run_decision(_ctx(
all_active=[inc], recent=[inc], talkgroup_name=TACTICAL_TG, all_active=[inc], recent=[inc], talkgroup_name=TACTICAL_TG,
)) ))
assert decision["action"] == "orphan"
def test_tactical_named_channel_still_attaches_inside_the_dispatch_window():
inc = _incident(idle_minutes=settings.tg_dispatch_thin_idle_minutes - 1)
decision = _run_decision(_ctx(
all_active=[inc], recent=[inc], talkgroup_name=TACTICAL_TG,
))
assert decision["action"] == "link" assert decision["action"] == "link"
assert decision["corr_debug"]["corr_path"] == "fast/thin" assert decision["corr_debug"]["corr_path"] == "fast/thin"