feat: consumer-session — TunnelSession (open/adopt, data planes, teardown)
- TunnelSession (src/consumer.rs): open (ChannelClient::open_channel)
+ adopt (ChannelManager::adopt_channel) construction, substrate-
shaped data planes (raw halves for stream; ADR-003 codec + pending
frame queue for UDP), pump_against (session-owned pump_bidi handle),
and teardown ownership per ADR-005: close (abort + ungraceful reap),
join (await + reap + copy counts, pump-less (0,0,reaped)), Drop
(abort + sync reap, never leaks). No Clone (compile_fail doc-test).
- open_reverse_channel on the CallConnection (the reverse path's
two-step: call then adopt — the POC's honest shape).
- Error surfaces (src/error.rs): TunnelOpenError (typed ADR-049 §4
surface via open_ref/establishment_reason), TunnelIoError (wrong-
substrate, TruncatedDatagram fail-loud, ChannelTaken), ReverseOpenError.
- Tests: tests/consumer_session.rs (12) over two harness topologies —
forward (wire_forward/ForwardTopology: consumer connect-side pure
client, producer serving via adapter) and reverse (the POC shape);
teardown matrix, W4 half-close, out-of-band close + self-reap (W3),
F-2 empty datagram, AdoptFailed-on-collision (bogus-id adopt parks
by design — documented), concurrent sessions. Harness: all_substrate_dial.
- Adopt takes substrate (task-note deviation: the data plane shape
rode the open call; cannot be inferred from the ID).
Verified: cargo test green (38), clippy -D warnings (native + wasm32),
fmt clean, cargo check --target wasm32-unknown-unknown passes.