v0.9.0-rc.23
parse_close_reason structured-variant refactor — closes peat-mesh#164. Replaces substring-on-Display matching in transport::iroh_mesh::IrohMeshConnection::parse_close_reason with an exhaustive structured match on iroh::endpoint::ConnectionError's 8 variants. No transport semantics, peer discovery, or wire-shape change. Documents the stable per-variant payload-tag contract on DisconnectReason::NetworkError / DisconnectReason::ApplicationError's public enum surface in src/transport/mod.rs.
Changed — parse_close_reason structured-variant match (peat-mesh#164)
transport::iroh_mesh::IrohMeshConnection::parse_close_reasonnow pattern-matches oniroh::endpoint::ConnectionErrorvariants directly instead of substring-matching theDisplaytext. Routing key moves from "does the formatted string contain 'timeout'/'reset'/'closed'/'application'?" to "which structured enum variant?". Three regression classes the legacy substring code was vulnerable to are now closed:thiserrorwording tweaks in upstream iroh / noq-proto would have silently shifted every disconnect into theNetworkError(_)fallback arm. Structured match is wording-independent.LocallyClosed↔RemoteClosedconfusion — the legacy code'scontains("closed")collapsed both intoRemoteClosed. The new match disambiguates:LocallyClosed→DisconnectReason::LocalClosed,ResetandConnectionClosed(_)→DisconnectReason::RemoteClosed.- New iroh variant addition — the exhaustive match in
map_connection_errorrefuses to compile if aConnectionErrorvariant is added upstream, surfacing the change at peat-mesh CI rather than after a downstream binary swallows it asNetworkError.noq-proto'sConnectionErroris not marked#[non_exhaustive]as of 1.0.0-rc.0, so exhaustive match is structurally enforceable.
VersionMismatchandCidsExhaustednow carry stable per-variant tags ("version-mismatch","cids-exhausted") in theNetworkErrorpayload instead of the rawDisplaytext. Audit-log parsers downstream stay stable across iroh wording changes.ApplicationClosed(_)payload format:"code={error_code} reason={utf8-lossy(reason)}"— gives operators a structured handle on the close-frame contents instead of an opaqueDisplay.- New unit test
transport::iroh_mesh::tests::parse_close_reason_per_variant_pincovers the 5 unit-shape variants. The 3 data-bearing variants (ConnectionClosed,ApplicationClosed,TransportError) can't be constructed from outsidenoq-proto(private/non-exhaustive struct internals), but the exhaustivematchinmap_connection_erroritself is the load-bearing regression guard for new variants. - Carryover from peat-protocol's pre-Phase-2
IrohMeshTransport; reviewer of peat-mesh#162 flagged this as a follow-up at the time and explicitly framed it as out-of-scope for that PR (CHANGELOG entry under 0.9.0-rc.21 / 0.9.0-rc.22 "Deferred (tracked separately)").
⚠ Consumer-visible behavior delta
Code that branches on DisconnectReason::RemoteClosed now sees fewer hits and DisconnectReason::LocalClosed sees more, because the legacy contains("closed") substring match collapsed iroh::endpoint::ConnectionError::LocallyClosed into RemoteClosed. Consumers that need to distinguish "we hung up" from "they hung up" — operational dashboards, reconnect-policy gates that suppress on local intentional close — should match LocalClosed separately now; consumers that previously branched only on RemoteClosed will see those locally-initiated closes route to a different arm. Same enum surface, different routing.
Stable payload tags on DisconnectReason::NetworkError and DisconnectReason::ApplicationError are now documented on the enum itself in src/transport/mod.rs — the per-variant tag contract ("version-mismatch", "cids-exhausted", "transport-error: {inner}", "code={n} reason={utf8-lossy(bytes)}") is part of the public surface, not an implementation detail of iroh_mesh. A format_application_close_payload_pin test pins the ApplicationError format directly via an extracted format_application_close(error_code, reason) helper.