Commit Graph
1 Commits
Author SHA1 Message Date
Claude 39e4f70e9d feat(channels): phoenix V2 JSON codec — v0.6 chunk 2
Json<T> newtype codec (src/channels/phoenix.rs), feature "phoenix":
speaks the Phoenix V2 wire format [join_ref, ref, topic, event, payload]
for any T: Serialize + DeserializeOwned. Replies encode as phx_reply
with the {"status", "response"} wrapper; payload-less control frames
encode payload as {}.

Deviation from roadmap sketch (documented in module docs): the roadmap
sketched a blanket Encode/Decode impl on bare P: Serialize. That is
coherence poison — with additive features, enabling "phoenix" anywhere
in the crate graph would forbid every user-written Encode impl. The
blanket therefore lives behind the Json<T> newtype: zero-boilerplate
(Deref/DerefMut/From), but opt-in per payload type.

Encode failure panics and rides the linked-teardown contract (channel
actor is linked to the conn; a payload that cannot serialize is a
programming error, not a protocol event). Decode strictness is exactly
T's Deserialize — use serde defaults or Json<serde_json::Value> for
lenient channels.

Integration (tests/integration.rs, cfg(feature = "phoenix")):
- channels_join_heartbeat_event_broadcast_leave: join ack ordering,
  conn-side heartbeat, unrouted-topic error, join rejection, broadcast
  incl. sender, correlated echo reply, leave -> ok + phx_close, and
  post-leave isolation.
- channels_codec_garbage_closes_1002: undecodable text frame closes
  the socket with protocol-error status.
- shutdown_with_open_channel_terminates: drain force-stop tears down
  the linked channel actor and the runtime reaches AllDone in budget.

Hammer: 35x lifecycle subset (+channels filter) + 3x full (phoenix) +
1x full (phoenix+smarm-trace) + featureless + channels-only, all green.
Suite: 84 unit + 45 integration + 2 doc under --features phoenix.
2026-06-12 14:56:26 +00:00