refactor(live-room): shrink right rail to slim Status strip#391
Merged
Conversation
Per ADR `docs/v0.10-chat-stream-vs-dashboard.md` §Q5, the right rail keeps a single ~10-line `Status` card (Work / Blockers / Evidence) and drops every per-spawn, per-turn, per-tool field. The slim rail is byte-identical across tool-call ticks because it no longer reads `state.spinners` or `state.spawn_lifecycle` — only `work_cards` statuses and the room-level `permission` flag. Closes #383.
9db4fa3 to
7eddcc8
Compare
spytensor
added a commit
that referenced
this pull request
May 26, 2026
Closes #381. Renders each `Working`-state spawn instance as an inline ASCII card spliced into the chat scrollback at the spawning event's chat-time position, per ADR Frame B. - New `working_card` module wraps the visual locked in the chat-stream-demo (#379) and reads only from `SpawnInstance`. Markers: `✓` done, `∴` in-progress, `⨯` failed. Identity color comes from `tui_style::role_color` — no hard-coded literals. - `SpawnInstance` gains a `title` field (populated from `WorkTitle` events the tracker now consumes) and a `chat_position` field (stamped by `RoomRuntimeState` at `TurnDispatched` time). - `render_scrollback` builds a merged scrollback that interleaves card lines at each spawn's `chat_position`, then applies the existing scroll-offset / wrap logic. Cards therefore scroll with history (ADR Q1 — no pinning) and the v0.9.16 `↓ N new` follow indicator continues to work. - Elapsed time renders at whole-second granularity so sub-second re-renders never change the rendered string (AC-5 flicker guard). - Long tool-call summaries middle-truncated to fit `card_width`. - Hotkey hint `[e]xpand [i]nterrupt [f]ocus` is rendered as a non-functional string per AC-7; `handle_key` is untouched (#385 will wire it). Addresses @Reviewer must-fix items on the original draft: 1. Rebased onto current main (which now has #390 footer narration and #391 rail slim). Tests verify the working card, footer narration row, and slim rail coexist at 120×30. 2. `push_scrollback` now calls `spawn_lifecycle.shift_chat_positions` after the 1000-row drain so every Working card's anchor index shifts left by `overflow` rows (saturating at 0). Without this the card would silently slip out of place after long sessions, which would have broken AC-2. Two new regression tests pin both: - `working_card_position_survives_scrollback_drain` floods 1500 rows while one card is Working, then asserts `chat_position` shifted and the card still precedes the latest line in the merged scrollback. - `build_merged_scrollback_is_idempotent` rerenders the same state twice and asserts byte-for-byte identical output. Validation: cargo fmt clean, cargo clippy --all-targets -- -D warnings clean, cargo test --lib console_room_runtime (77 pass — 65 existing + 6 footer narration + 6 working card integration), cargo test --lib working_card (21 pass), cargo test --lib spawn_lifecycle (17 pass).
spytensor
added a commit
that referenced
this pull request
May 26, 2026
Closes #381. Renders each `Working`-state spawn instance as an inline ASCII card spliced into the chat scrollback at the spawning event's chat-time position, per ADR Frame B. - New `working_card` module wraps the visual locked in the chat-stream-demo (#379) and reads only from `SpawnInstance`. Markers: `✓` done, `∴` in-progress, `⨯` failed. Identity color comes from `tui_style::role_color` — no hard-coded literals. - `SpawnInstance` gains a `title` field (populated from `WorkTitle` events the tracker now consumes) and a `chat_position` field (stamped by `RoomRuntimeState` at `TurnDispatched` time). - `render_scrollback` builds a merged scrollback that interleaves card lines at each spawn's `chat_position`, then applies the existing scroll-offset / wrap logic. Cards therefore scroll with history (ADR Q1 — no pinning) and the v0.9.16 `↓ N new` follow indicator continues to work. - Elapsed time renders at whole-second granularity so sub-second re-renders never change the rendered string (AC-5 flicker guard). - Long tool-call summaries middle-truncated to fit `card_width`. - Hotkey hint `[e]xpand [i]nterrupt [f]ocus` is rendered as a non-functional string per AC-7; `handle_key` is untouched (#385 will wire it). Addresses @Reviewer must-fix items on the original draft: 1. Rebased onto current main (which now has #390 footer narration and #391 rail slim). Tests verify the working card, footer narration row, and slim rail coexist at 120×30. 2. `push_scrollback` now calls `spawn_lifecycle.shift_chat_positions` after the 1000-row drain so every Working card's anchor index shifts left by `overflow` rows (saturating at 0). Without this the card would silently slip out of place after long sessions, which would have broken AC-2. Two new regression tests pin both: - `working_card_position_survives_scrollback_drain` floods 1500 rows while one card is Working, then asserts `chat_position` shifted and the card still precedes the latest line in the merged scrollback. - `build_merged_scrollback_is_idempotent` rerenders the same state twice and asserts byte-for-byte identical output. Validation: cargo fmt clean, cargo clippy --all-targets -- -D warnings clean, cargo test --lib console_room_runtime (77 pass — 65 existing + 6 footer narration + 6 working card integration), cargo test --lib working_card (21 pass), cargo test --lib spawn_lifecycle (17 pass).
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.
Closes #383.
Implements ADR
docs/v0.10-chat-stream-vs-dashboard.md§Q5 — the right rail collapses to a singleStatuscard (Work / Blockers / Evidence, ~10 rendered lines including borders) and stops reading anything that ticks per tool-call.Summary
render_status_railto delegate to a new pureslim_status_rail_lines(state) -> Vec<Line>that reads onlywork_cardsstatuses and the room-levelpermissionflag — nostate.spinners, nostate.spawn_lifecycle.rail_work_active_count(Working WorkCards),rail_blocker_status(room-level permission/interrupted),rail_evidence_status(no read ofhas_active_work, which would have leaked spinner state in).status_panel_lines,work_card_lines,role_metadata_line,work_status_label,status_color_for_work,work_card_detail,evidence_status_text,evidence_status_color. Kept the still-usefulsection_headerandstatus_kv_line.RoomRuntimeState::teamandlast_seenas#[allow(dead_code)]with doc comments explaining the back-compat reason (the rail'sactivity: K/N roles seenfield that consumed them is gone — moved to the footer narration per v0.9.16: footer narration bar — "N roles still working" #382 / Q4).activecounts WorkCards not spinners, blockers track only room-level signals, the rail never renders the WorkCard body.Removed-fields relocation table
Per AC-3. Every field the old rail carried is either resurfaced on another v0.10 surface or deliberately dropped as redundant.
activity: K/N roles seenWorkblockN roles still working · @role @roleis the live answer; the K/N counter is dropped as redundant with the per-role listlatest: <work card title>Workblockassignee: @role · workingWorkblock@role · <title> · working · 14sStatusapproval: @role · <tool>Blockersblockstate: approval pendingat room level; the per-role / per-tool detail lives in the existing permission overlay which is unchangedinterrupted: @role · <reason>Blockersblock@role ⨯ interrupted · {elapsed} · {N steps}; the slim rail keeps a room-levelstate: interruptedsummaryvalidation: observed complete | pending | blockedEvidenceblockEvidenceblock (renamed semantics) — nowclean | pending | blocking | not observedper ADR §Q5. Derivation no longer readshas_active_work()(which inspectedspinners), onlywork_cardsstatuses andpermission@rolework-card identity headerAC status
AC-1 (no per-spawn lifecycle reads from rail):
grepagainst the file shows zero matches insiderender_status_rail,slim_status_rail_lines,rail_work_active_count,rail_blocker_status, orrail_evidence_status. The matches that remain are:use crate::spawn_lifecycle::{SpawnInstance, SpawnLifecycleTracker};— required for the state fieldapply_event— not on the rail render pathVerified by
grep -E 'spawn_lifecycle|SpawnInstance|working_instances|spawning_instances' src/console_room_runtime.rs.AC-2 (rail stable across tool-call ticks): new test
rail_stays_byte_identical_across_spinner_ticksconstructs twoRoomEvent::Spinnerevents differing only onframe,tools_seen,current_stateand asserts the rendered rail body is identical. The implementation makes this hard to regress: the new helpers only readwork_cardsandpermission, both of which are quiet on spinner events.AC-3 (field relocation table): above.
AC-4 (rail tests deleted not adapted): see "Test impact" — three tests are deleted with a comment in place; one is rewritten to express the new locked semantics.
AC-5 (main Room reclaims freed space): the rail's
Constraint::Length(42)is intentionally kept. Rationale: the slim rail content is much narrower than 42 (max ~20 cols), but shrinking the rail to e.g. 32 would (a) re-trigger layout regressions tested by existing snapshots without a behavioral payoff, and (b) leave awkward whitespace if/when ADR §Q5 evolves. The visual "freed space" the rail used to consume vertically (tool-call tree, work-card body) is the real win — the Room now uses that height for chat-stream working cards (v0.9.16: inline working card widget in the chat stream #381). The body'sConstraint::Min(62)already provides the Room with all the horizontal room it needs at supported viewports. Layout constraints inrender_room_runtime_frameare untouched per the issue's instructions.Test impact
rail_shows_work_status_when_no_spinners_and_no_cardsrail_shows_locked_slim_status_when_idle, keptcards: 0and the absence of removed fieldsrail_shows_status_and_center_current_turn_when_spinningrail_active_counts_working_work_cards_not_spinners@backendbehavior. New test additionally pins the ADR semantic that a spinner alone leavesactive: 0rail_folds_work_panel_when_no_cardsWorkblock; folding never happens. Covered byrail_shows_locked_slim_status_when_idleandrail_does_not_render_work_card_bodyrail_renders_team_plus_work_when_card_present_but_no_spinnerrail_does_not_render_work_card_bodylatest,assignee, the inline◇ @backendheader, and the work-card title on the rail — every one of those surfaces is gone per ADR §Q5work_card_renders_role_label_exactly_once_per_card@backendinside the rail-rendered work card body, which the rail no longer renders. Per AC-4: delete, not adaptwork_and_spinner_events_populate_status_railRun validation) and role glyph (◇) appeared on the rail. Those surfaces moved to the chat-stream working card (#381)work_cards_render_a_role_identity_header_per_card◇ @backendon the rail above the card; the rail no longer renders cardsrail_stays_byte_identical_across_spinner_ticksrail_active_counts_working_work_cards_not_spinnersactiveis WorkCards, not spinnersrail_does_not_render_work_card_bodyrail_blocker_state_reflects_permission_promptValidation
Test plan
crsession with two concurrent sub-agents, watch the rail across many tool-call ticks: confirm no flicker inactive/cards/validation/state(AC-2 live dogfood).WorkCard-emitting flow (e.g. host validation) and confirmactiveincrements by exactly 1,cardsincrements by exactly 1, and neither moves on per-spinner ticks afterwards.Blockers state: approval pendingwithout any role/tool detail on the rail (the overlay still carries that detail).120×30to the ADR Frame A mockup — the rail should match.