fix(dial): bare-nick dial surfaces the same unsendable reason wire send emits#354
Merged
Merged
Conversation
…end` emits A peer can be pinned in trust yet unsendable: its relay endpoint's slot_token stays empty until the peer's pair_drop_ack lands (common right after pairing, an MCP/daemon restart, or when we're not federation-reachable so the ack can't arrive). `wire send` already classifies this precisely and returns an actionable `peer_unknown` reason. But both dial surfaces — CLI `cmd_dial` and MCP `tool_dial` — short-circuited a pinned peer to a bland `already_pinned` with no sendability check, so an operator/agent whose sends were bouncing would re-dial the bare nick forever, never seeing the cause (the loop a real cross-machine session hit). Extract one shared classifier `send::unsendable_reason(peer) -> Option<String>` (None = a usable route exists; Some = the same reason the send path emits) and route all three surfaces through it so they can't drift: - `delivery_json` PeerUnknown arm now calls it (output preserved exactly). - `cmd_dial` PinnedPeer arm pushes a `warning` step when unsendable; the human printer shows the detail inline instead of a bare "warning" label. - `tool_dial` PinnedPeer arm adds `sendable: bool` + an optional `reason` (status stays `already_pinned` — additive, no consumer break). The classifier mirrors the send loop's routability exactly: an HTTP endpoint counts only when relay_url + slot_id + slot_token are all non-empty, and a peer reachable only over Nostr (recorded nostr_transport + a local nostr key, the RFC-007 D3 fallback) reads as sendable — so the dial warning never contradicts what a send would actually do. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Deploying wireup-landing with
|
| Latest commit: |
8bb40dd
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://8c2400bf.wireup-landing.pages.dev |
| Branch Preview URL: | https://fix-dial-surfaces-unsendable.wireup-landing.pages.dev |
…IFIED Follow-up to the same root cause. `resolve_name_to_target` — the shared dial/whois resolver — passed the raw stored `tier` straight through, so a dial or whois on a pinned-but-not-yet-acked peer reported `VERIFIED` while `wire status`/`wire peers` (which already use `trust::effective_tier`) reported `PENDING_ACK` for the same peer at the same moment. The dial surfaces contradicted every other surface. Reuse the existing state instead of inventing a parallel one: the resolver now computes `effective_tier` (the same VERIFIED-vs-PENDING_ACK calculation the rest of the system uses), so dial/whois/status/peers all agree. Combined with the `sendable`/`reason` transport detail from the prior commit, a dial now reads coherently — relationship tier AND can-I-send-now, both honest. No change to `effective_tier` itself (a trust primitive) — only its caller. Regression test locks it: a VERIFIED pin with an empty reply-slot token resolves to PENDING_ACK, and flips to VERIFIED once the token lands. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Jun 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A peer can be pinned in trust yet unsendable: its relay endpoint's
slot_tokenstays empty until the peer'spair_drop_acklands (common right after pairing, an MCP/daemon restart, or when we're not federation-reachable so the ack can't arrive).wire sendalready classifies this and returns an actionablepeer_unknownreason — but both dial surfaces short-circuited a pinned peer to a blandalready_pinnedwith no sendability check.Result: an operator/agent whose sends were bouncing
peer_unknownwould re-dial the bare nick forever, seeing a reassuringalready_pinnedeach time and never the real cause. (This is the exact loop a real cross-machine session hit — bare-nick re-dial →already_pinned→ send →peer_unknown, repeat.)Fix
Extract one shared classifier
send::unsendable_reason(peer) -> Option<String>(None= a usable route exists;Some= the same reason string the send path emits) and route all three surfaces through it so they can't drift:delivery_jsonPeerUnknown arm now calls it — output preserved exactly.cmd_dial(CLI) PinnedPeer arm pushes awarningstep when unsendable; the human printer shows the detail inline (WARN: …) instead of a barewarninglabel.tool_dial(MCP) PinnedPeer arm addssendable: bool+ an optionalreason.statusstaysalready_pinned— additive, no consumer break.The classifier mirrors the send loop's routability exactly:
relay_url+slot_id+slot_tokenare all non-empty (matches the send loop'scontinueskip — a token on a malformed endpoint is not a route);nostr_transport+ a local nostr key, the RFC-007 D3 fallback) reads as sendable, so the dial warning never contradicts what awire sendwould actually do.Tests
send::unsendable_reason_reads_live_statecovers: not-pinned → Some; usable HTTP slot → None; pinned-but-empty-token → Some("no token yet"); Nostr-reachable → None; malformed endpoint (token but empty relay_url/slot_id) → Some. Full lib suite: 611 pass, fmt + clippy -D warnings clean.🤖 Generated with Claude Code