Citywide/large systems (e.g. NYPD Citywide) never get a location anchor, so location_proximity never fires and duplicate incidents get created for the same event at nearby addresses #159
Closed
opened 2026-09-20 19:51:47 -04:00 by logan
·
1 comment
No Branch/Tag Specified
Labels
Clear labels
area/ai-pipeline
area/auth
area/billing
area/correlation
area/discord
area/frontend
area/governance
area/growth
area/infra-deploy
area/legal
area/map
area/mqtt
area/node-ota
area/osint
area/recording-audio
area/sdr-radio
area/transcription
board
board:agenda
minutes:draft
minutes:final
role:ceo
role:cfo
role:cio
role:ciso
role:cmo
role:coo
role:cto
Scene/entity extraction, summarizer, AI providers, embeddings
AuthN/Z, roles, tenancy/org scoping, API keys, credentials
Plans, entitlements, AI spend accounting and caps
Incident correlation, linking, lifecycle, severity, corr_debug
Discord bot, radio bots, voice streams, bot tokens, webhooks
Web UI: Next.js app and the edge-node local dashboard
Board process, minutes, unattended runner, agent tooling
Go-to-market, outreach, cold email, discovery conversations
CI/CD, VM, registry, Firestore rules/indexes deploy, Terraform/Caddy
ToS, retention, licences/DAAs, CAN-SPAM, E&O, name suppression
Live map: pins, paths, tiles, legend, geocoding
Node-C2 MQTT, checkins, commands, node sweeper, dynsec
Node install, enrollment, OTA updater, image skew
External feeds: cameras, 511, ADS-B, AIS, outages, and their licences
Call capture, boundaries, PulseAudio, ring buffer, trimming, attribution
OP25 decode, SDR hardware/tuning, conventional, second SDR, mesh radio
STT, transcript correction, place verification, name accuracy
Board process: minutes and board-filed items
Needs board scoping at the next sitting; CEO removes the label once scoped
One exec pre-read, closed by CEO
CEO ruling; open until enacted
Filed by the CEO
Filed by the CFO
Filed by the CIO
Filed by the CISO
Filed by the CMO
Filed by the COO
Filed by the CTO
kind
behavior-bug
Wrong product behavior; the instrument reads it correctly
kind
measurement-bug
Corrupts the recording/instrument (debug data, evals, health checks), not behavior
priority
p0
Drop everything: prod broken, live exposure, or owner-ruled P0
priority
p1
Next up: blocks a gate, a customer, or core incident quality
priority
p2
Normal: real defect or planned work, schedule in turn
priority
p3
Low: minor, cosmetic, or someday
status
blocked
Waiting on an external party, spend, or another issue
status
in-progress
Branch or PR open against it
status
needs-decision
Waiting on an owner or board ruling before work
status
needs-info
Root cause or repro unknown; needs a reading first
type
bug
Something built is wrong: code, data, or a measurement
type
chore
Maintenance, refactor, tests, pinning, cleanup: no user-visible change
type
decision
A ruling to make or record: board minutes, owner calls, legal reviews
type
docs
Docs, runbooks, CLAUDE.md, skills, SOPs
type
feature
New capability or an extension of an existing one
type
security
Security, privacy, credential or tenant-isolation exposure
type
tracking
Tracker, checklist, initiative or punch-list spanning several changes
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: logan/server-26#159
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Live evidence, prod, 2026-09-20 ~23:00 UTC, via /admin Correlation Debug: two incidents 106 seconds apart on the same talkgroup (
New York City - NYPD Citywide 2 Patch, TG 8026, system5426129d-378e-4186-b583-ad5306747aa5):874ea52a"Shots Fired at 1108 Jackson Avenue" (22:58:42) — suspect: male black, black hoodie/jacket/sweatshirt, jeans, knee pads, red stripes on the bottomf776ef6b"Shots Fired at Jackson Avenue" (23:00:34) — suspect: male Black, slim build, black jacket, black sweats, red stripes on the bottom of the [pants]Same block, matching and unusually specific suspect description (red stripes on the bottom), 2 minutes apart — this reads as the same real event reported twice, filed as two separate incidents. Both calls have
location_coords: null. The rules engine AND the Gemini tier both independently returnednewfor the second call; the LLM's owncorr_llm_reasoningwas "which is a distinctly different location from all active incidents" — comparing the strings "1108 Jackson Avenue" and "Jackson Avenue" as text, with no coordinates to check.Root cause, traced through
app/internal/area_context.pyandplace_verifier.py:resolve_anchor()geocodes the system's configured municipality into{center, radius_km}, but discards the result whenradius_km > settings.area_anchor_max_radius_km("an anchor wider than the max is not written at all" — deliberate, per the module's own docstring, to avoid a radius so wide it "would confirm anything inside it"). "New York City" geocodes to a viewport far wider than any reasonablearea_anchor_max_radius_km, so this system permanently has no anchor.place_verifier.py's own docstring: "NO ANCHOR MEANS SKIP, NOT ACCEPT" — without an anchor, place verification (and the geocode that would producelocation_coords) is skipped entirely, for every call, forever, on this talkgroup.Consequence for
incident_correlator.py:location_coordsnever populates for this system, so thelocation_proximityfit signal (_call_fits_incidentpath 2, and the disambiguation weight in_disambiguate) can never fire. Every substantive call on a citywide system is reduced to unit_overlap-or-nothing, and the LLM tiebreak is asked to judge location purely as free text, which it gets wrong on exactly the case that matters most (a general street reference vs. a specific number on that street). Pulled from the same debug dump: every incident on TG 8026 (NYPD Citywide) in the sample haslocation_coords: null; incidents on smaller single-municipality systems (e.g.Ossining - Police Dispatch, TG 9600) do get real coordinates ({"lat": 41.1467702, "lng": -73.8653459}on "320 Spring Street"). This is systemic, not a one-off geocoder miss.This is the same
corr_pathdistribution the correlation debug endpoint already surfaces platform-wide (this 80-incident sample: 78newvs 28fast/thin+ 15fast/disambig+ 2fast/single+ 2location, andmedian_calls_per_incident: 1) — a large fraction of "new" decisions on a big city system are calls that a human would recognize as the same event, but that have no coordinate to check against.Citywide/regional systems are exactly the ones with the highest call volume and the most concurrent incidents on one talkgroup — the population location_proximity exists to disambiguate — and they are structurally the ones that can never have it.
Not the same bug as server-26#158 (srcaddr unused in the thin-call path) — that's under-attachment of context-free acks to an existing incident; this is over-splitting of two substantive, information-bearing calls that should have merged.
Possible directions, not prescribing one: a sub-municipality anchor (borough/precinct/sector, whichever the radio system's config already carries) narrow enough to pass
area_anchor_max_radius_km; a per-call point geocode (no anchor needed to sanity-check, just to getlocation_coordsfor the proximity math) with a separate, size-appropriate sanity radius for large systems; or teaching the LLM prompt to actually diff address-with-number vs. address-without-number as a fuzzy same-location signal instead of free-text guessing blind.Fixed in
66bbf5b, pushed to main and live in prod (verified via /health git_sha). 422 pass, 0 fail. Also manually merged the two duplicate prod incidents this bug produced ("Shots Fired at 1108 Jackson Avenue" call reattached to "Shots Fired at Jackson Avenue" via the Archive page's attach/detach; the emptied duplicate auto-resolved). Follow-up filed at #160 (same gap in place_verifier.py, not fixed here).