eddf5f57a5
Driver.Mqtt.Browser-002/-003/-004/-005. -002: ObservePlainTopic's pre-pass judged segment LENGTH only, so a plain topic segment carrying a newline or an ANSI escape became the picker's displayed label — a rule ObserveSparkplugBirth already applied via IsUsableLabel. The control-character half is now single-sourced (HasControlCharacter) and the plain marker text converges with the Sparkplug one. An EMPTY topic level stays accepted on purpose: it is legal MQTT and its terminal leaf is a real committable address. -003: GetOrCreate read _nodeCount and incremented it later, so N observers could each see cap-1 and all insert. The slot is now reserved with one Interlocked.Increment and given back on both non-consuming paths. -004: Project ordered a level by Segment alone, but the level is keyed by Path precisely because two Sparkplug siblings CAN share a Segment — leaving the tie to ConcurrentDictionary enumeration order, which .NET's randomised string hashing makes differ per process. .ThenBy(Path) makes the sort total. -005: no behaviour change — DefaultNodeCap's doc now states the real per-session ceiling and records that the aggregate across sessions is bounded by the reaper's idle TTL, not by any session cap.