forked from unom/punktfunk
`abi.rs`'s header states "panics never cross the boundary: every entry point is wrapped in `catch_unwind`". Of its 78 `extern "C"` entry points, 16 were not. Ten of those are fine and were always fine — `punktfunk_abi_version` returns a constant, and the nine `punktfunk_connect*` shims forward every argument unchanged into a guarded implementation — but five ran real code bare: `session_free`, `connection_close`, `connection_disconnect_quit`, `reanchor_gate_free`, `reanchor_gate_arm`. The three `*_free`/`close` ones are the point. They run `Drop` for an entire `Session` or `Connection` — transports, threads, mutexes — and a `Drop` impl that unwraps a poisoned lock panics. Since Rust 1.81 that unwind is a hard abort rather than undefined behaviour, so this is not a soundness hole; it is worse-behaved than it looks. Aborting the CALLER'S process because one of our teardown paths hit a poisoned mutex is not an acceptable failure mode for a library, and it would present as "the app died in punktfunk_session_free" with no Rust backtrace to explain it. Adds `guard_void`, the sibling of `guard` for entry points with no status to report through: it catches, logs, and returns — right for teardown, where the object is going away regardless. The five are wrapped in it. The header now says what is actually true, including WHICH entry points are deliberately bare and why, so the next reader can check the claim instead of trusting it. That is the same failure this sweep found in `MappedView`'s `Sync` proof: a stated invariant that a reviewer would rely on and that had quietly stopped holding. Verified: Linux .21 fmt + both CI clippy steps rc=0, and the C ABI harness — which exercises the `*_free`/`close` paths it touches — still passes, 4 frames byte-exact through lossy loopback.