146 sites, 141 of them in `abi.rs`, the `extern "C"` surface `cbindgen` turns into
`punktfunk_core.h`. Writing 141 independent arguments would have been the wrong answer and would
have read like it: they are instances of ONE contract in six shapes — opaque handles reached only
through `as_mut()`/`as_ref()` (so null becomes a status, never a dereference), caller-owned
out-params (null-checked exactly where the header calls them optional), NUL-terminated-or-null C
strings through `opt_cstr`, unchanged forwarding to a versioned entry point, fixed-length output
buffers, and `addr_of!` reads.
So the contract is stated once at the top of the file, the way `pf-win-display`'s CCD contract is,
and each site says which instance it is. Two properties that hold everywhere are stated there and
not repeated 141 times: no pointer is retained past the call that received it, and every entry point
runs inside `guard`'s `catch_unwind`, so a panic becomes a status code instead of unwinding into C.
The `addr_of!` sites got a real proof rather than a shape, because that one is load-bearing and
non-obvious: it forms a raw pointer WITHOUT creating a reference precisely because the caller's
struct may be an older, smaller version, so the field must be read by offset and not through a `&`.
`transport/udp/linux.rs`'s five are genuinely individual — two `zeroed()` POD initialisations, and
the `sendmmsg`/`recvmmsg`/`CMSG` trio, where the argument worth writing down is that
`msg_controllen` is set to `CMSG_SPACE(size_of::<u16>())` and the `CmsgBuf` is 64 bytes, so the
kernel cannot write past the control buffer.
punktfunk-core now denies `undocumented_unsafe_blocks`. Verified: Linux .21 fmt + both CI clippy
steps rc=0, and — since this is the C surface and comments alone should not change it — the C ABI
harness still passes, 4 frames round-tripped byte-exact through lossy loopback.