correlator: drop the dispatch/tactical name-guess from the escape hatch, use one window (#115)

Owner correction from direct scanning experience: a talkgroup named tac/tactical only sees materially different traffic during a real incident, and that's rare -- the bulk of traffic on any monitored channel, including high-risk stops and pursuits, runs on the main channel regardless of what it's named. _is_dispatch_channel's string match on the talkgroup name is a naming-convention guess, not a detector of actual channel behavior; trusting it here meant a busy single-channel department not literally named 'dispatch' would silently get the more permissive 15-minute window and could reproduce #115's original bug (the gate never firing on the channels it targets).

Always use tg_dispatch_thin_idle_minutes (5 min) in the escape hatch, regardless of talkgroup name. Does NOT touch incident_correlator.py's own fast/thin idle-window selection, which uses the same dichotomy for a different, decision-changing purpose -- bigger blast radius, left for its own review.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01Tbknwttzou4s46PAykmtix
This commit is contained in:
Logan Cusano
2026-09-13 14:07:00 -04:00
co-authored by Claude Sonnet 5
parent 833cfade4e
commit c50bfda8db
2 changed files with 44 additions and 32 deletions
+21 -14
View File
@@ -118,14 +118,25 @@ def _recent_incident_on_same_talkgroup(ctx: dict) -> bool:
case: the ack carries no substance of its own but plainly belongs to the
job just opened.
The window mirrors whatever the fast/thin path would use for this same
channel — `tg_dispatch_thin_idle_minutes` (5 min) on a dispatch backbone,
`tg_thin_idle_minutes` (15 min) on a tactical/working channel, selected via
the same `_is_dispatch_channel` test incident_correlator.py uses at its own
fast/thin idle-window selection (~:1005-1007). Using the dispatch constant
unconditionally would be wrong off dispatch — a retune of one for fast/thin
reasons would then silently widen or narrow this gate too, on channels
window #3 never measured.
Always uses `settings.tg_dispatch_thin_idle_minutes` (5 min), regardless
of what the talkgroup is named. An earlier version of this branched on
`_is_dispatch_channel` (mirroring incident_correlator.py's fast/thin idle-
window selection) to use a longer 15-minute window on anything not
literally named "dispatch"/"patched"/"primary" — owner correction,
2026-09-13, from direct scanning experience: a talkgroup named "tac"/
"tactical" genuinely does see materially different traffic only during a
real incident, and that's rare; the overwhelming majority of traffic on
ANY monitored channel — including high-risk stops and pursuits — runs on
the main channel regardless of what it's named. `_is_dispatch_channel`'s
string-match is a naming-convention guess, not a detector of actual
channel behavior, and trusting it here meant a busy single-channel
department not literally named "dispatch" would silently get the more
permissive window and reproduce #115's original bug. One constant,
applied uniformly, is the safer default; a real low-volume channel is the
rare case, not the norm, so erring toward the tighter window costs little.
NOT applied to incident_correlator.py's own fast/thin selection (out of
scope for this pass — a bigger, decision-changing surface, worth its own
review rather than changing under this fix).
This used to be a plain "does any recent incident exist on this
talkgroup" check against a 2-hour window (`correlation_window_hours`).
@@ -160,7 +171,7 @@ def _recent_incident_on_same_talkgroup(ctx: dict) -> bool:
# status, no capacity filter) if a future measurement window pins a real
# gate miss on a resolved/capped same-talkgroup incident.
"""
from app.internal.incident_correlator import _idle_gate_minutes, _is_dispatch_channel
from app.internal.incident_correlator import _idle_gate_minutes
tg_id = ctx.get("talkgroup_id")
system_id = ctx.get("system_id")
@@ -168,11 +179,7 @@ def _recent_incident_on_same_talkgroup(ctx: dict) -> bool:
return False
tg_str = str(tg_id)
now = ctx.get("now") or datetime.now(timezone.utc)
idle_limit = (
settings.tg_dispatch_thin_idle_minutes
if _is_dispatch_channel(ctx.get("talkgroup_name"))
else settings.tg_thin_idle_minutes
)
idle_limit = settings.tg_dispatch_thin_idle_minutes
for inc in ctx.get("recent") or []:
if system_id not in (inc.get("system_ids") or []):
continue