feat(repl): introduce pluggable RoomSink for runtime I/O#326
Merged
Conversation
Stage 1 of hosting the executable REPL runtime inside the full-screen ratatui room. Adds a renderer-agnostic sink trait so the same runtime can drive either the legacy stdout path (`cr start`) or a future TUI host without code duplication. - `src/room_io.rs`: `RoomEvent` enum, `RoomSink` trait, `StdoutSink` impl that preserves byte-for-byte legacy output, `stdout_sink()` factory, in-tree unit tests. - `repl::run_with_options` is now a thin wrapper around `run_with_options_and_sink(root, opts, sink)`. The runtime threads `Arc<dyn RoomSink>` through `send_and_drain` → `drain_one_turn_handling_ctrl_c` → `drain_one_turn` → `render_event`. - Diagnostic surfaces (`cr show`, `/transcript`) inline `StdoutSink` — they are always stdout-bound. - Incidental: clear the four `ratatui::Frame::size` / `Buffer::get` deprecation warnings introduced by the 0.30 bump (#324) so the strict `clippy -D warnings` gate stays green. `cr start` output is byte-for-byte unchanged: `StdoutSink` calls the same `render_event_line` formatter and the same `println!`. The threading is the load-bearing change; subsequent issues port the remaining ~160 stdout sites and add the TUI sink. Refs #320
This was referenced May 25, 2026
The ratatui 0.30 bump in #324 transitively pulled darling 0.23 which requires rustc 1.88. main has been red since that PR merged. This bump unblocks CI without rolling back ratatui. Cargo.toml `rust-version` and rust-toolchain.toml `channel` move together so local builds and CI stay aligned.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stage 1 of hosting the executable REPL runtime inside the full-screen ratatui room (#320). Adds a renderer-agnostic
RoomSinktrait so the samerepl::run_with_optionsexecution path can drive either the legacy stdout (cr start) or a future TUI host with no logic duplication.cr startoutput is byte-for-byte unchanged; the change is structural only.Architecture impact
docs/architecture.mdas written.docs/proposed-amendments.md.docs/proposed-amendments.mdand reference it here.The runtime contract (when roles run, when CrepEvents publish, when permission prompts fire) is unchanged. Only the visible-event delivery surface gains a trait boundary.
Linked issues
Refs #320 (full-screen runtime parity). Unblocks the four follow-up issues opened alongside this PR (Stages 2–5).
Acceptance criteria
This PR is Stage 1 of #320 — it does not by itself satisfy any AC of #320. It puts the trait in place so AC-1..AC-7 can be tackled in parallel.
Arc<dyn RoomSink>end-to-end,cr startconstructsStdoutSinkand prints identically to today.Test plan
cargo fmt --all -- --checkpassescargo clippy --all-targets --all-features -- -D warningspassescargo test --all-features --lockedpasses (594 lib + all integration)shellcheck spike/*.shpasses — N/A, no shell changescr startbyte-for-byte unchanged by construction —StdoutSink::emitcalls the samerender_event_lineformatter and the sameprintln!the legacy code did).Evidence packet
src/room_io.rs(new, 179 LOC):RoomEvent,NoticeLevel,RoomSinktrait,StdoutSinkimpl,stdout_sink()factory, 2 unit testssrc/lib.rs(+1): registerroom_iosrc/repl.rs(+38):run_with_options→ thin wrapper; newrun_with_options_and_sink(root, opts, sink);send_and_drain/drain_one_turn_handling_ctrl_c/write_journalgainsinkparameter;/transcriptinlinesStdoutSinksrc/repl/render.rs(+13):render_eventsignature gainssink: &dyn RoomSink, body delegates tosink.emit(RoomEvent::Crep)src/repl/turn.rs(+6):drain_one_turngainssink: Arc<dyn RoomSink>; 4render_eventcall sites pass it throughsrc/repl/show.rs(+6):cr show(diagnostic command) inlinesStdoutSinksrc/console_tui.rs(+4): incidental — clear 4 ratatui 0.30 deprecation warnings introduced by chore(deps)(deps): bump ratatui from 0.27.0 to 0.30.0 #324 soclippy -D warningsstays greencargo fmt --all -- --checkcargo clippy --lib --tests --all-targets -- -D warningscargo test --lib→ 594 passed, 0 failedcargo test --tests→ all integration suites passedcr startunder a real engine subprocess was not manually re-run (covered by integration tests and by construction since the formatter is identical)Tracker update
The current active tracker is #238 per
AGENTS.md(or the v0.9 milestone tracker — verify and update). Stage 1 is structural; no tracker checkbox flips until Stages 2–5 land.Implementation status:
Stage 1 complete; #320 still requires Stages 2-5 (separate issues opened).Risk and rollback
StdoutSink::emitfor the only currently-modeled event variant (Crep) calls the same formatter and sameprintln!the legacy code did. No control-flow change. 594 unit tests + all integration tests pass unchanged.Out of scope
output::*,splash::*,work::render_card,status::StatusRegion,permission_prompt::*) through the sink. Tracked in follow-up issues.TuiSinkor any ratatui-side renderer for runtime events. Tracked in a follow-up issue.LiveRoomBridge/apply_action_to_snapshot. Tracked in a follow-up issue.crentrypoint. Stays on the legacy path until full-screen runtime parity is verified.