Skip to content

fix(tui): complete sub-agent detail panel state coverage + artifact handles (#4094) - #4197

Merged
Hmbown merged 1 commit into
mainfrom
codex/v0868-fix-4094
Jul 8, 2026
Merged

fix(tui): complete sub-agent detail panel state coverage + artifact handles (#4094)#4197
Hmbown merged 1 commit into
mainfrom
codex/v0868-fix-4094

Conversation

@Hmbown

@Hmbown Hmbown commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Fixes #4094

Completes the four items the reopen comment listed as still-open after PR #4182 (which shipped the live-activity trail, tool-call name/status, final summary/handoff, and bounded histories). Single-file change to crates/tui/src/tui/sidebar.rs (the detail-panel renderer #4182 added): three test additions, two small width-safety fixes they exposed, and one bounded artifact-handle feature.

The four remaining items

1. Many-tool-call state coverage + tests. A worker that has fired hundreds of tool calls now provably renders correctly — non-empty, carrying the live tool-call trail (name + compact status) and the step count, width-bounded, plus an inspect-more handle. Distinct from #4182's existing many-agents bound test.

  • Test: subagent_expanded_detail_renders_many_tool_calls_without_overflow — asserts the dossier line carries 247 step(s) and grep_files, no blank lines, and (rendered narrow) no line overflows the column.

2. Narrow-width readability + tests. Every rendered line — header, role-mix, label, dossier, handle — is now clamped to the column. This exposed and fixed two real overflows:

  • the dossier line's format!(" {}", truncate(.., width-2)) overflowed by the 2-space indent at content_width < 3 → new indented_detail_line() clamps indent + body together;
  • the two-tone N running / M header was never truncated and spilled past narrow columns → it now collapses to a single truncated span when it doesn't fit (status clipped, never spilled).
  • Test: subagent_detail_readable_and_bounded_across_narrow_widths — asserts no line overflows across widths [1,2,3,5,8,12,16,20,24,32,48] and that the status verb stays visible at a usable-narrow width (24).

3. Explicit running/done/failed state matrix + tests.

  • Test: subagent_status_matrix_renders_marker_verb_and_style — for running/done/failed/canceled/interrupted, asserts the label renders the right marker ([~]/[✓]/[!]/[-]), the right state color on the span, and that the dossier surfaces the status verb.

4. Artifact/receipt handle for full output (vs. inline dump). Instead of dumping the full sub-agent transcript inline (the freeze/emptiness risk this issue tracks), the panel renders a bounded preview plus a copyable handle to the fuller artifact: ▸ full output · handle_read agent:<id>/full_transcript. This reuses the existing, documented sub-agent transcript var-handle — the same agent:<id> / full_transcript handle that subagent_session_projection materializes and that default_subagent_artifacts documents as the inspect path — so it adds no new artifact infrastructure. The handle is gated (subagent_output_handle) so an empty transcript is never advertised, guarded by max_rows, and truncated so it never overflows.

  • Tests: subagent_completed_worker_surfaces_output_handle_not_inline_dump (completed worker shows the transcript handle and still shows the bounded summary; handle line is non-clickable), subagent_output_handle_gated_on_inspectable_output (fresh zero-step worker → no handle; running-with-steps and terminal workers → handle).

Scope of item 4 (transparent boundary)

This is a bounded feature per the issue's feasibility gate — a copyable handle reference the user runs via the existing handle_read tool, reusing existing plumbing. It intentionally does not add a new artifact system. Optional future enhancements (not required by the AC, flagged for the maintainer): making the handle line click-to-open the transcript in-TUI, and surfacing richer receipt kinds (patch / test_result / report) beyond the transcript handle.

Notes

Verification

  • cargo fmt --all --check → clean
  • cargo clippy --workspace --all-features --locked -D warnings (gate allow-list) → clean
  • cargo test --workspace --locked → 5996 passed; the only 2 failures are the known pre-existing unrelated ones (git_repo_root_reports_attempted_paths_when_no_repo_found, skill_hotbar_action_activates_skill_through_dollar_alias), confirmed failing on pristine origin/main with this change stashed
  • cargo build --release -p codewhale-tui → ok

…andles (#4094)

Closes out the four items that reopened #4094 after PR #4182 delivered the
live-activity trail, tool-call name/status, final summary, and bounded
histories.

1. Many-tool-call coverage: a single worker that has fired hundreds of tool
   calls renders non-empty, carries the live tool-call trail + step count,
   stays width-bounded, and exposes an inspect-more handle. New test:
   subagent_expanded_detail_renders_many_tool_calls_without_overflow.

2. Narrow-width readability: every rendered line (header, role-mix, label,
   dossier, handle) is now clamped to the column. Fixes a real overflow: the
   old `format!("  {}", truncate(.., width-2))` and the two-tone running/done
   header both spilled past the column at narrow widths. Adds
   indented_detail_line() (indent-aware clamp) and a header width-clamp. New
   test: subagent_detail_readable_and_bounded_across_narrow_widths (widths
   1..48, plus status-verb-visible at a usable-narrow width).

3. running/done/failed matrix: asserts each state renders the right marker,
   status verb, and state color. New test:
   subagent_status_matrix_renders_marker_verb_and_style.

4. Artifact/receipt handle for full output: instead of dumping the full
   transcript inline (the freeze/emptiness risk), the panel shows a bounded
   preview plus a copyable handle to the fuller artifact --
   `handle_read agent:<id>/full_transcript` -- reusing the documented
   sub-agent transcript var-handle (subagent_session_projection /
   default_subagent_artifacts). Gated so an empty transcript is never
   advertised, guarded by max_rows, and truncated so it never overflows. New
   tests: subagent_completed_worker_surfaces_output_handle_not_inline_dump,
   subagent_output_handle_gated_on_inspectable_output.

The handle is the one place a raw agent id is intentionally surfaced in the
detail panel (a functional, copyable handle on its own line), keeping the
dossier line id-free per #3030.

Verified: cargo fmt --all --check; cargo clippy --workspace --all-features
--locked -D warnings (gate allows); cargo test --workspace --locked (only the
two known pre-existing unrelated failures, confirmed on origin/main); cargo
build --release -p codewhale-tui.

Authored by Hunter with agent assistance.
Copilot AI review requested due to automatic review settings July 8, 2026 04:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@claude

claude Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 3m 32s —— View job


Review: PR #4197 — sub-agent detail panel state coverage + artifact handles

Summary

Single-file, well-scoped diff to crates/tui/src/tui/sidebar.rs. The narrow-width overflow fixes (indented_detail_line, the header-collapse guard) and the status-matrix/many-tool-call tests are solid, targeted fixes for real bugs the reopen comment called out — good width-safety discipline (content_width.max(1) used consistently, no unwrap/expect added, no panics found).

Bug: advertised output handle doesn't exist for running workers (posted inline at sidebar.rs:2766)

subagent_output_handle (sidebar.rs:2766) returns Some("agent:{id}/full_transcript") whenever steps_taken > 0, including for running workers — this is exactly what the PR's own subagent_expanded_detail_renders_many_tool_calls_without_overflow test exercises (a running row, steps_taken: 247, asserting the handle line appears).

But agent:<id>/full_transcript is only ever populated by insert_subagent_full_transcript_handle (crates/tui/src/tools/subagent/mod.rs:4551), which is called exclusively from terminal branches (Cancelled/Interrupted/BudgetExhausted/end-of-run) — never while a worker is still running. The codebase's own subagent_session_projection (mod.rs:3174-3192) makes this explicit: it only reuses the full_transcript handle if snapshot.status != SubAgentStatus::Running, and inserts a differently-named "transcript" handle otherwise.

So for a running worker, calling handle_read on the panel's advertised handle fails with "handle_read: no payload found for handle agent:<id>/full_transcript" (crates/tui/src/tools/handle.rs:281-286). That's worse than the "empty transcript" case the PR says it guards against — the handle is nonexistent, not empty, for the running-worker case that's actually most common while a worker is expanded and active. The new unit tests can't catch this because they only assert against the SidebarAgentRow struct in isolation, never against the real handle store.

Suggested fix: gate the handle on sidebar_agent_status_is_terminal(row.status) only, or point running workers at the live agent:<id>/transcript name to match what subagent_session_projection actually inserts mid-run.

Minor notes (not blocking)

  • agent_status_marker/sidebar_agent_status_is_terminal are pre-existing and unchanged by this diff — not in scope, but worth noting sidebar_agent_status_is_terminal checks for a literal "budget" status string while subagent_status_name actually emits "budget_exhausted" (pre-existing, unrelated to this PR).
  • No provider/model-prefix inference issues found — this diff doesn't touch model/provider selection.
  • No security concerns (no new input parsing, no secrets, no shell/exec paths touched).

Comment on lines +2766 to +2771
fn subagent_output_handle(row: &SidebarAgentRow) -> Option<String> {
let has_output = sidebar_agent_status_is_terminal(row.status.as_str()) || row.steps_taken > 0;
if !has_output {
return None;
}
Some(format!("agent:{}/full_transcript", row.id))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: the advertised handle doesn't exist yet for running workers.

subagent_output_handle returns Some("agent:{id}/full_transcript") whenever steps_taken > 0, even while the worker is still running (see the subagent_expanded_detail_renders_many_tool_calls_without_overflow test, which exercises exactly this: a running row with steps_taken: 247).

But the backing var-handle agent:<id>/full_transcript is only ever inserted by insert_subagent_full_transcript_handle (crates/tui/src/tools/subagent/mod.rs:4551, called only from the Cancelled/Interrupted/BudgetExhausted/end-of-loop terminal branches — mod.rs:5038,5168,5233,5331,5609). The codebase's own subagent_session_projection (mod.rs:3174-3192) is explicit about this: it only looks up the full_transcript handle if snapshot.status != SubAgentStatus::Running; otherwise it inserts a differently-named "transcript" handle instead.

So for a running worker, handle_read on the advertised agent:<id>/full_transcript reference will fail with "handle_read: no payload found for handle agent:<id>/full_transcript" (crates/tui/src/tools/handle.rs:281-286). That directly contradicts the PR's stated guarantee ("gated ... so an empty transcript is never advertised") — it's not empty, it's nonexistent. The new unit tests only assert against the SidebarAgentRow struct in isolation, so they can't catch this mismatch against the real handle store.

Suggested fix: only advertise the handle once the worker is terminal (sidebar_agent_status_is_terminal(row.status)), or point running workers at the live agent:<id>/transcript name instead of full_transcript to match what subagent_session_projection actually inserts while running.

[Fix this →](https://claude.ai/code?q=Fix%20subagent_output_handle%20in%20crates%2Ftui%2Fsrc%2Ftui%2Fsidebar.rs%20(around%20line%202766%2C%20branch%20codex%2Fv0868-fix-4094)%3A%20it%20currently%20returns%20Some(%22agent%3A%7Bid%7D%2Ffull_transcript%22)%20whenever%20steps_taken%20%3E%200%2C%20including%20for%20running%20workers.%20But%20the%20agent%3A%3Cid%3E%2Ffull_transcript%20var-handle%20is%20only%20inserted%20by%20insert_subagent_full_transcript_handle%20in%20crates%2Ftui%2Fsrc%2Ftools%2Fsubagent%2Fmod.rs%20at%20terminal%20states%20(Cancelled%2FInterrupted%2FBudgetExhausted%2Fend-of-loop)%2C%20never%20while%20running%20(see%20subagent_session_projection%20around%20mod.rs%3A3174%2C%20which%20explicitly%20branches%20on%20status%20%21%3D%20Running%20before%20looking%20up%20full_transcript%2C%20falling%20back%20to%20a%20differently-named%20%22transcript%22%20handle%20otherwise).%20Fix%20subagent_output_handle%20to%20only%20advertise%20the%20full_transcript%20handle%20when%20sidebar_agent_status_is_terminal(row.status)%20is%20true%2C%20and%20update%2Fadd%20tests%20accordingly%20(the%20many-tool-call%20running-worker%20test%20should%20no%20longer%20expect%20a%20full_transcript%20handle%20line%2C%20or%20should%20expect%20no%20handle%20at%20all%20while%20running).

@Hmbown
Hmbown merged commit 01cf217 into main Jul 8, 2026
19 checks passed
@Hmbown
Hmbown deleted the codex/v0868-fix-4094 branch July 24, 2026 21:10
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.

Sub-agent detail panel is empty and can freeze the TUI during active work

2 participants