Inbound wire messages are now delivered FROM the proxy representing the
remote sender, so local actors see a replyable `from`. This enables full
two-way communication across the wire:
- endpoint: deliver send_named/send via proxy (deliver_named/deliver_pid)
instead of sending raw; from_id=0 bypasses proxy for anonymous sends
- proxy: handle deliver_named and deliver_pid to send from within actor
scope (providing from-substitution); cache one owned pid clone per sender
keyed by stable instance_id() to avoid use-after-free when forwarding
reply handles to the endpoint asynchronously
- test: add remote_endpoint_id_test covering the full inbound proxy table /
from-substitution / outbound ID table / send-by-ID round-trip
- test: extend remote_child_endpoint with echo_id actor and send_wire_by_id
to support the new test
- Proxy passes sender's handle pointer as from_id instead of 0
- Endpoint assigns stable wire IDs to local actors on first send, keyed by handle pointer
- Reverse table (wire_id → pid) enables inbound send routing to local actors by ID
- Full bidirectional send-by-ID is now functional end-to-end
- Spawn a local Proxy actor on first message from each new remote actor ID
- Route inbound wire exit to the corresponding proxy
- Clean up proxy table entries on proxy_exit notification
- Teardown sends {"exit", "transport_closed"} to all live proxies
Actors may be destroyed without ever calling their receivers. This is
regular behavior when an actor is killed by an exit message. C++ actors
cleanup automatically via their destructors. Up to now zig actor had to
enable trapping and cleanup on the exit message. This was a big foot gun
and cumbersome.
Now all zig actors are required to pass a deinit function to
thespian.receive. This simplifies clean up and prevents the foot gun
entirely.