2b849aed7a
Lands the broadcast layer of F15. Session::connect_nmx now starts a local CallbackExporter on an OS-assigned ephemeral port, builds a callback OBJREF advertising it (using local_hostname() with a 127.0.0.1 fallback), and registers that OBJREF with NmxClient::register_engine_2 (was register_engine_2_without_callback). A router task drains the exporter's CallbackEvent stream, decodes each CallbackInvoked body as NmxSubscriptionMessage, and broadcasts parsed messages on a tokio::sync::broadcast channel. Per-subscription correlation routing — turning these raw messages into per-Subscription DataChange streams — is the next iteration's work. F15 stays open until that lands. New Session API - Session::callbacks() -> broadcast::Receiver<Arc<NmxSubscriptionMessage>>: raw observable of every parsed callback message. Test seam + escape hatch for consumers that need raw access today. - Session::callback_exporter_addr() -> Option<SocketAddr>: returns the exporter's local addr (Some until shutdown_nmx, None after). SessionInner additions - callback_exporter: Mutex<Option<CallbackExporter>> — taken in shutdown. - callback_tx: broadcast::Sender<Arc<NmxSubscriptionMessage>>. - router_handle: std::sync::Mutex<Option<JoinHandle<()>>>. shutdown_nmx now performs the full cleanup chain: 1. UnregisterEngine over the live NMX transport. 2. CallbackExporter::shutdown (cancels accept loop). 3. Wait for router task — exits naturally once exporter's mpsc sender side closes. Std::sync::Mutex guard taken-out-then-dropped before await to avoid clippy::await_holding_lock. Routing rationale (callback_router fn) - CallbackEvent::CallbackInvoked → parse via NmxSubscriptionMessage::parse_inner → broadcast Arc<msg>. - Other event variants (Bind / Auth3Ignored / ProtocolError / etc.) silently dropped at this layer; consumers needing them can listen to a future diagnostic-channel hook (no followup yet). - Parse failures silent — the .NET reference fires a separate UnparsedCallbackReceived event we don't model yet. Cargo.toml: added mxaccess-callback as a direct dep on mxaccess. Tests (5 new in mxaccess; total 35) - callbacks receiver observes injected NmxSubscriptionMessage. - multi-subscriber broadcast hands out the same Arc to each receiver. - callback_exporter_addr is Some before shutdown, None after. - router_task end-to-end: feed a hand-built CallbackInvoked event with a 39-byte 0x32 SubscriptionStatus body, observe the parsed message on the broadcast. - router silently drops non-CallbackInvoked events (e.g. Bind). Test count delta: 506 -> 511 (+5). All four DoD gates green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>