Halve the tier-2 thin-call window, from 10 minutes to 5
With over-creation fixed, the incidents that remain are readable enough to judge, and the ones that still do not make sense all fail the same way. A content-free call attaches to the single active incident on its talkgroup if that incident has been idle under tg_dispatch_thin_idle_minutes, and at 10 minutes that is long enough for the channel to have moved on to something else. In the 00:30Z dump a "72 at Holland Station" incident absorbed a Grand Central train-crew meet 9.6 minutes later, and a status check absorbed a records lookup at 9.7. Being the only candidate is not evidence. It means the channel was quiet, which is exactly when guessing is weakest -- the single-candidate rule was meant to avoid picking wrongly among several, not to license a match no other signal supports. Every correct thin attach in that dump was <= 3.4 minutes idle and every wrong one was >= 8.2, so 5 separates them with room on both sides. Real back-and-forth is unaffected: it runs through the 30-second tier-1 path, and the observed conversational replies sit near zero. Tests pin both sides of the new boundary at 4.9 and 5.1 minutes so a later change to this number has to be deliberate. 23 pass. Also corrects a DEFERRED.md entry written earlier today. It claimed nothing ever closes an incident that goes quiet; summarizer.py has run a stale sweep at incident_auto_resolve_minutes (90) the whole time. The 37 open incidents were caused by over-creation, not by a missing sweeper, and 90 minutes may be fine now -- worth rechecking on a fully post-fix dump before changing it. No new environment variables: tg_dispatch_thin_idle_minutes is a config.py default and is not templated into any .env, 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
0bd92269d2
commit
7b5258cfdf
@@ -51,7 +51,14 @@ class Settings(BaseSettings):
|
||||
unit_continuity_max_idle_minutes: int = 20 # unit-continuity path: skip if incident idle > this
|
||||
recorrelation_scan_minutes: int = 60 # re-examine orphaned calls ended within this window
|
||||
tg_fast_path_idle_minutes: int = 90 # fast path: max minutes since incident last updated
|
||||
tg_dispatch_thin_idle_minutes: int = 10 # dispatch channels only: thin calls only attach to incidents idle < this many minutes
|
||||
# Dispatch channels only: tier-2 thin calls attach to a lone candidate idle < this.
|
||||
# Was 10, which is long enough for the channel to have moved on to something else:
|
||||
# on 2026-08-16 a "72 at Holland Station" incident absorbed a Grand Central train
|
||||
# meet 9.6 min later, and a status check absorbed a records lookup at 9.7 min.
|
||||
# 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
|
||||
# back-and-forth is handled by the 30-second tier-1 path above this.
|
||||
tg_dispatch_thin_idle_minutes: int = 5
|
||||
|
||||
# Vocabulary learning
|
||||
vocabulary_induction_interval_hours: int = 24 # how often the induction loop runs
|
||||
|
||||
@@ -146,6 +146,24 @@ def test_thin_call_links_to_the_active_incident_on_its_talkgroup():
|
||||
assert decision["corr_debug"]["corr_path"] == "fast/thin"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("idle_min", [1.0, 3.4, 4.9])
|
||||
def test_thin_call_still_attaches_inside_the_tier2_window(idle_min):
|
||||
inc = _incident(idle_min)
|
||||
assert _run_decision(_ctx(all_active=[inc], recent=[inc]))["action"] == "link"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("idle_min", [5.1, 8.2, 9.7])
|
||||
def test_thin_call_does_not_attach_after_the_channel_has_moved_on(idle_min):
|
||||
"""
|
||||
A '10-4' arriving many minutes into silence is new traffic, not a reply. The
|
||||
old 10-minute window let one incident swallow an unrelated event 9.6 min
|
||||
later; being the *only* candidate is not evidence, it just means the channel
|
||||
was quiet, which is when the guess is weakest.
|
||||
"""
|
||||
inc = _incident(idle_min)
|
||||
assert _run_decision(_ctx(all_active=[inc], recent=[inc]))["action"] == "orphan"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_thin_link_does_not_refresh_updated_at():
|
||||
with patch("app.internal.incident_correlator.fstore") as mock_fstore:
|
||||
|
||||
Reference in New Issue
Block a user