ffe5b43d8d54c9e3291c6c90cfdd512ab070d0e7
Field-testing the reconnect fix surfaced a follow-on bug: clicking Disconnect (or quitting) showed the peer as yellow "Reconnecting" on the other end instead of leaving cleanly. Logs showed the remote saw NeighborDown first and the gossip `Leave` arrived ~15s later (broadcast then router torn down ~450ms after, so NeighborDown — now meaning "reconnecting" — beats the slow, swarm-routed Leave). Fix: make graceful leave a prompt, reliable TRANSPORT signal instead of relying on gossip. On leave/quit, IrohTransport::leave() explicitly closes each live connection with a distinguished goodbye code (GOODBYE_CODE) and aborts all supervisors (so none linger redialing the about-to-close endpoint). The remote's supervisor inspects the close reason: an application close with our goodbye code => emit the new ConnEvent::Left (evict now); a timeout/reset/other code => transient drop, reconnect as before. core handles ConnEvent::Left exactly like a PeerLeft (cancel grace timer, disconnect, drop jitter, UI remove). ActiveSession now holds the transport and calls leave() before shutting the router down. Tracks the live connection per peer in Shared.live_conns (inserted on link up, removed on drop) so leave() can close them. The gossip Leave path stays as a harmless backup. New test dialer_reports_left_on_graceful_close: a close with the goodbye code reports Left and does NOT reconnect (mirror of the code-0 transient-drop test). clippy clean, 4 transport tests pass. NOT yet field-verified. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>