feat(channel): drive a Channel through a recv/handle/send loop (#564)#672
feat(channel): drive a Channel through a recv/handle/send loop (#564)#672real-venus wants to merge 1 commit into
Conversation
…Pod#564) Add `serve_channel`, the loop that owns a single Channel end to end: `recv` a turn, route it through a per-turn handler, `send` the response, repeat until the channel closes. `ChannelRegistry` deliberately does not drive channels and `ScriptedChannel` exists to exercise wiring without sockets, but nothing connected the two -- this is that missing primitive. Error handling splits along the transport/application seam. A handler (application) error is per-turn: it is logged and the turn skipped, so one bad turn does not tear the channel down. A `send` (transport) error is treated as a dead channel and ends the loop. Returns the number of responses delivered. Existing transport behavior is unchanged; this only adds the shared driver they will adopt when ported onto Channel (GeniePod#564).
matedev01
left a comment
There was a problem hiding this comment.
LGTM — adds serve_channel, the recv→handle→send driver the channel module's docs already pointed to (ChannelRegistry deliberately doesn't drive; ScriptedChannel exercises wiring). Error split is right: handler (application) errors are per-turn (logged, skipped), send (transport) errors are fatal and end the loop; returns the delivered count. Incremental step on #564, no transport behavior change. Verified: clippy -D warnings clean, channel unit tests (19) + serve_channel doctest pass, fmt clean.
|
Reviewed and the driver loop itself is good — approving in spirit. The only thing blocking merge is a CHANGELOG.md conflict from other PRs that landed since (the serve.rs/mod.rs code merges clean). Please rebase onto |
What changed
Adds
genie_core::channel::serve_channel, therecv → handle → senddriver loop over theChanneltrait. A transport now only implementsChanneland supplies a per-turn handler;serve_channelowns the loop. This is the primitive the module's own docs point to —ChannelRegistryexplicitly does not drive channels, andScriptedChannelexists to exercise wiring withoutsockets, but nothing tied them together.
send(transport) errors are fatal: the loop stops and returns the error.Resolving #564 incrementally (channel-driver step for M2). No existing transport behavior changes — this only adds the shared loop the voice/HTTP/Telegram handlers will adopt when ported onto
Channel.Real Behavior Proof
cargo fmt --all -- --check— clean (Linux, rust 1.96)cargo clippy --workspace --all-targets --locked -- -D warnings— cleancargo clippy -p genie-core -p genie-ctl --no-default-features --all-targets --locked -- -D warnings— clean (chat-only axis)cargo test --workspace --locked --all-targets— passing, incl. 4 new tests inchannel::servecargo test --workspace --locked --doc—serve_channeldoctest compilesNew tests in
crates/genie-core/src/channel/serve.rs:drives_every_queued_turn_and_records_responses— three queued turns delivered in orderclosed_channel_delivers_nothing— empty inbox returns 0handler_error_skips_the_turn_without_stopping_the_channel— a failing turn is skipped, others still deliveredsend_error_is_fatal_and_stops_the_loop— a transport send failure ends the loop after one attemptVerified on x86_64 Linux (rust 1.96) reproducing the CI job set. Not Jetson-tested: this is a transport-agnostic control-flow addition with no hardware surface.