Skip to content

feat(live-room): footer narration bar for sub-agent activity#390

Merged
spytensor merged 1 commit into
mainfrom
feat/v0.10-382-footer-narration
May 26, 2026
Merged

feat(live-room): footer narration bar for sub-agent activity#390
spytensor merged 1 commit into
mainfrom
feat/v0.10-382-footer-narration

Conversation

@spytensor
Copy link
Copy Markdown
Owner

Closes #382.

Summary

Adds a one-row FooterNarration strip 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.

  • The strip is hidden entirely when zero sub-agents are in Working and zero are in Spawning — the composer reclaims that row.
  • The leading N roles still working count tracks only Working instances per the v0.10 ADR (docs/v0.10-chat-stream-vs-dashboard.md, "Footer narration line"). Spawning roles are named with a trailing · @role spawning chip but do not increment the count, because they have no live tool-call stream yet.
  • Role chips use tui_style::role_color so colors match the @role mentions in scrollback.
  • When the row would overflow, the tail truncates with … +N more. The truncation marker reserves its own space so the last chip that fits is never followed by a half-drawn chip.
  • No "chat resumes when they report" wording — per ADR Q4 the line lists roles only, since v0.10 does not gate composer input on sub-agent activity.

Visuals (test-asserted strings)

Case Rendered
Zero (hidden) (strip not rendered — composer reclaims the row)
One Working 1 role still working · @backend
Many Working fit 2 roles still working · @security @backend
Working + Spawning 1 role still working · @security · @backend spawning
Zero Working + Spawning 0 roles still working · @qa spawning
Overflow truncated 12 roles still working · @ingestor00 … +11 more

Acceptance criteria (#382)

  • AC-1: New FooterNarration strip rendered above the composer. One terminal row, no border. (render_footer_narration + conditional 5-slot layout in render_room_runtime_frame.)
  • AC-2: Content derived from SpawnLifecycleTracker via working_instances_ordered_by_started_at() and spawning_instances_ordered_by_started_at(). Working drives the count + names; Spawning drives the · @x spawning suffix.
  • AC-3: Role chips colored with tui_style::role_color(role, host_role). Asserted in footer_narration_chips_use_identity_colors.
  • AC-4: When zero Working and zero Spawning the strip is not rendered — composer keeps its cursor row, verified by footer_narration_hidden_when_no_subagents.
  • AC-5: Pluralization — 1 role still working for one Working, N roles still working (incl. 0 roles) otherwise.
  • AC-6: Overflow truncates with … +N more; verified renderer never exceeds the row budget.
  • AC-7: Renderer tests cover four cases — zero / one / many-fits / many-truncate — plus the locked Spawning-suffix rule and the identity-color rule.

Validation

$ cargo fmt --all -- --check         # clean
$ cargo build --lib --quiet          # clean
$ cargo test --lib console_room_runtime --quiet
running 71 tests
.......................................................................
test result: ok. 71 passed; 0 failed
$ cargo test --lib spawn_lifecycle --quiet
running 14 tests
..............
test result: ok. 14 passed; 0 failed
$ cargo test --test console_terminal_qa_test --quiet
running 5 tests
.....
test result: ok. 5 passed; 0 failed
$ cargo clippy --all-targets --all-features -- -D warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s)
$ cargo test --lib --quiet
test result: ok. 721 passed; 0 failed; 1 ignored

Test plan

  • cargo fmt --all -- --check
  • cargo build --lib --quiet
  • cargo test --lib console_room_runtime --quiet
  • cargo test --lib spawn_lifecycle --quiet
  • cargo test --test console_terminal_qa_test --quiet
  • cargo clippy --all-targets --all-features -- -D warnings
  • Full cargo test --lib --quiet (721 passing, 1 ignored)

Scope discipline

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 spytensor merged commit 2c3a906 into main May 26, 2026
5 checks passed
@spytensor spytensor deleted the feat/v0.10-382-footer-narration branch May 26, 2026 09:18
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).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

v0.9.16: footer narration bar — "N roles still working"

1 participant