Swap _devices and _tagsByName to ConcurrentDictionary so ShutdownAsync Clear() no longer races concurrent TryGetValue calls; store ProbeTask on DeviceState and await it in ShutdownAsync before disposing the client and gate, eliminating the probe-disposal race. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>