feat(live-room): footer narration bar for sub-agent activity#390
Merged
Conversation
Adds a one-row narration strip above the composer that names the roles currently being waited on while sub-agents work. Per the v0.10 ADR (`docs/v0.10-chat-stream-vs-dashboard.md`, "Footer narration line"), the leading `N roles still working` count tracks only instances in the `Working` state; `Spawning` instances are named with a trailing `· @ROLE spawning` suffix chip that does not increment the count, because spawning roles have no live tool-call stream yet and the user has nothing to interact with. The strip is hidden entirely when both counters are zero so the composer reclaims the row. Closes #382.
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 #382.
Summary
Adds a one-row
FooterNarrationstrip rendered between the body and the composer in the live room. It names the roles currently being waited on while sub-agents work, so the chat going quiet on a long delegation stops reading like the room is broken.Workingand zero are inSpawning— the composer reclaims that row.N roles still workingcount tracks onlyWorkinginstances per the v0.10 ADR (docs/v0.10-chat-stream-vs-dashboard.md, "Footer narration line").Spawningroles are named with a trailing· @role spawningchip but do not increment the count, because they have no live tool-call stream yet.tui_style::role_colorso colors match the@rolementions in scrollback.… +N more. The truncation marker reserves its own space so the last chip that fits is never followed by a half-drawn chip.Visuals (test-asserted strings)
(strip not rendered — composer reclaims the row)1 role still working · @backend2 roles still working · @security @backend1 role still working · @security · @backend spawning0 roles still working · @qa spawning12 roles still working · @ingestor00 … +11 moreAcceptance criteria (#382)
FooterNarrationstrip rendered above the composer. One terminal row, no border. (render_footer_narration+ conditional 5-slot layout inrender_room_runtime_frame.)SpawnLifecycleTrackerviaworking_instances_ordered_by_started_at()andspawning_instances_ordered_by_started_at(). Working drives the count + names; Spawning drives the· @x spawningsuffix.tui_style::role_color(role, host_role). Asserted infooter_narration_chips_use_identity_colors.footer_narration_hidden_when_no_subagents.1 role still workingfor one Working,N roles still working(incl.0 roles) otherwise.… +N more; verified renderer never exceeds the row budget.Validation
Test plan
cargo fmt --all -- --checkcargo build --lib --quietcargo test --lib console_room_runtime --quietcargo test --lib spawn_lifecycle --quietcargo test --test console_terminal_qa_test --quietcargo clippy --all-targets --all-features -- -D warningscargo test --lib --quiet(721 passing, 1 ignored)Scope discipline
render_status_rail(v0.9.16: shrink right rail to scoped dashboard, move team activity out #383's surface) or scrollback rendering (v0.9.16: inline working card widget in the chat stream #381's surface).handle_key(no new interactions, no chip clicks/hover per v0.9.16: footer narration bar — "N roles still working" #382 out-of-scope).Constraint::Length(5).