Context
`src/client/socket_manager.rs:64`:
```rust
/// TODO: narrow `source` to `SocketAddrV4` to match the `TransportSocket`
/// trait's IPv4-only contract — today the field is always a
/// `SocketAddr::V4(_)` wrapping, and the V6 variant is unreachable.
/// Deferred because the rename ripples through `DiscoveryMessage` and
/// `ClientUpdate::Unicast`.
```
Goal
Narrow `ReceivedMessage::source` from `core::net::SocketAddr` to `core::net::SocketAddrV4` so the type-system contract honestly reflects the IPv4-only scope of the `TransportSocket` trait. The V6 arm in pattern-matching call sites is dead today.
Scope
The rename ripples through:
- `ReceivedMessage::source` itself.
- `DiscoveryMessage` (carries the same field type).
- `ClientUpdate::Unicast` (publicly exposes it).
- All pattern-match sites that destructure `SocketAddr::V4(...)` to extract the inner `SocketAddrV4`.
Migration
Breaking change for any downstream code that pattern-matches on `ClientUpdate::Unicast { source: SocketAddr::V4(_) }`. After: `source: SocketAddrV4` directly.
Context
`src/client/socket_manager.rs:64`:
```rust
/// TODO: narrow `source` to `SocketAddrV4` to match the `TransportSocket`
/// trait's IPv4-only contract — today the field is always a
/// `SocketAddr::V4(_)` wrapping, and the V6 variant is unreachable.
/// Deferred because the rename ripples through `DiscoveryMessage` and
/// `ClientUpdate::Unicast`.
```
Goal
Narrow `ReceivedMessage::source` from `core::net::SocketAddr` to `core::net::SocketAddrV4` so the type-system contract honestly reflects the IPv4-only scope of the `TransportSocket` trait. The V6 arm in pattern-matching call sites is dead today.
Scope
The rename ripples through:
Migration
Breaking change for any downstream code that pattern-matches on `ClientUpdate::Unicast { source: SocketAddr::V4(_) }`. After: `source: SocketAddrV4` directly.