QUALITY-671: roll up orchestration credit usage in the agent-mode footer#11048
Conversation
Aggregate credits spent by an orchestrator's descendant child agents into the parent's agent-mode footer and collapsed pill, behind the new `FeatureFlag::OrchestrationCreditRollup` dogfood flag. Highlights: - New `compute_orchestration_rollup` helper produces per-agent breakdown rows (orchestrator first, then descendants by spawn order) and a total credits figure aggregated across the locally-loaded conversation tree. - Lift the existing descendant walker into shared `app/src/ai/blocklist/orchestration_topology.rs` reused by the rollup and the pill bar. - Extend `ConversationUsageView` with a "View details" / "Hide details" toggle and per-agent breakdown rows (first 5 + "Show N more"). Reuse the pill bar's avatar helpers for row icons. - Emit `BlocklistAIHistoryEvent::ConversationUsageMetadataUpdated` from the usage-metadata update path so the footer and parent collapsed pill re-render live when any descendant spends credits. - Wire the rollup into the expanded footer constructor and the collapsed pill `render_usage_button` (rollup total drives the headline credit number + suppression check; the existing `(+N)` annotation is unchanged). Includes specs at `specs/QUALITY-671/`. Co-Authored-By: Oz <oz-agent@warp.dev>
The usage footer was created via ctx.add_view in handle_usage_footer_toggled, which does not register the view's TypedActionView handler with the framework. Clicks on "View details" / "Show N more" dispatched ConversationUsageViewAction::* via ctx.dispatch_typed_action, but the framework logged "Dispatched action has no handlers" and silently dropped the action. Switch to ctx.add_typed_action_view so ConversationUsageView::handle_action runs as expected and the toggle/expand state flips on click. Add conversation_usage_view_tests.rs to lock in the handler logic — exercises ToggleDetailsExpanded and ShowAllAgentRows directly through view.update so a future regression in either handler arm (or the TypedActionView impl itself) fails at test time. Co-Authored-By: Oz <oz-agent@warp.dev>
…toggle as a hyperlink Two visual fixes for the orchestration credit rollup: 1. The per-agent breakdown rows were being appended at the end of the usage card (after "LAST RESPONSE TIME"), so they read as a disconnected section instead of as a drill-down of "Credits spent (total)". Refactor the rollup-row insertion into a small helper and call it immediately after pushing the Credits spent (last response) / total pair, so the rows sit directly beneath the value they elaborate on. 2. The "View details" / "Hide details" toggle and the "Show N more" link were rendered in the sub-text color, which reads as a passive label rather than a clickable affordance. Switch both to theme.ansi_fg_blue() - the same color FormattedTextElement uses for in-line hyperlinks throughout Agent Mode (see PromptAlertView). The Hoverable's PointingHand cursor remains the on-hover affordance; we don't flip the color or weight on hover because Text doesn't expose an underline knob and changing color here tends to push the link into the accent space. Co-Authored-By: Oz <oz-agent@warp.dev>
…alignment Visual tweaks for the orchestration credit rollup breakdown: 1. Agent names now render in the same color as the "USAGE SUMMARY" header (text_disabled) so the rollup rows read as a sub-list of that section rather than competing with primary labels. 2. Per-agent credit values now render in the "Credits spent" label color (text_sub) instead of the primary value color, visually echoing the label they elaborate on. 3. Agent names are clipped at PER_AGENT_LABEL_MAX_WIDTH (= the pill bar's PILL_LABEL_MAX_WIDTH) with ellipsis, so long child-agent names can't push the credit-value column off-screen. 4. "Show N more" was breaking the two-column flex alignment because the value-side slot was an Empty (0 height) while the label side carried a real Text element. Subsequent rows then drifted out of step with their labels. Push a single-space Text placeholder opposite the link so the columns stay row-aligned through the rest of the card. Co-Authored-By: Oz <oz-agent@warp.dev>
|
I'm starting a first review of this pull request. You can view the conversation on Warp. I completed the review and no human review was requested for this pull request. Comment Powered by Oz |
There was a problem hiding this comment.
Overview
This PR adds a feature-flagged orchestration credit rollup for the agent-mode footer and collapsed usage pill, with shared topology traversal, aggregation helpers, and footer UI state for the breakdown.
Concerns
- The rollup currently activates when only the orchestrator has credits and all descendants are idle, which contradicts the intended descendant-credit gating and adds a misleading one-row breakdown.
- The new link-style controls are implemented as mouse-only hover/click elements, so the required keyboard activation path for View details and Show N more is missing.
Verdict
Found: 0 critical, 2 important, 0 suggestions
Request changes
Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).
Powered by Oz
| } | ||
| } | ||
|
|
||
| if entries.is_empty() { |
There was a problem hiding this comment.
None when that count is zero.
There was a problem hiding this comment.
Children don't really remain idle, so not really a big deal.
| } else { | ||
| ("View details", Icon::ChevronDown) | ||
| }; | ||
| Hoverable::new( |
There was a problem hiding this comment.
Hoverable plus .on_click makes the View/Hide details control mouse-only, and the Show N more link below uses the same pattern. Use a focusable/button-style element or add keyboard handling so both controls are reachable in tab order and activatable with Enter/Space as required.
There was a problem hiding this comment.
The rest of the usage box is not accessible by keyboard, so going to defer this one.
The rollup is self-gating: compute_orchestration_rollup returns None when the orchestrator has no locally-loaded descendants or no credits, so the expanded footer and collapsed pill naturally fall through to today's exact behavior whenever the data isn't there. The dedicated flag was redundant on top of FeatureFlag::OrchestrationV2 (which gates whether a user can create child agents in the first place) and FeatureFlag::AgentView (which gates the expanded footer surface), so remove it and update PRODUCT.md / TECH.md to reflect the self-gating contract. Co-Authored-By: Oz <oz-agent@warp.dev>
| // (invariant 9), or a new descendant was spawned | ||
| // (invariant 8). Trigger a re-render; the actual rollup | ||
| // is recomputed from the history model in `render`. | ||
| ctx.notify(); |
There was a problem hiding this comment.
Wondering if it's possible to restrict this to certain conversation events rather than any conversation event across the app?
i.e. this type of thing
let parent_id = parent_conversation_id;
ctx.subscribe_to_model(&history_model, move |_, history, event, ctx| {
let touched_id = match event {
BlocklistAIHistoryEvent::ConversationUsageMetadataUpdated { conversation_id }
| BlocklistAIHistoryEvent::RemoveConversation { conversation_id, .. }
| BlocklistAIHistoryEvent::DeletedConversation { conversation_id, .. } => *conversation_id,
_ => return,
};
// history.read takes (&AppContext, F) — collect the bool before notifying
// so the immutable borrow ends before ctx.notify() needs &mut.
let should_notify = history.read(ctx, |h, _| {
touched_id == parent_id
|| descendant_conversation_ids_in_spawn_order(h, parent_id).contains(&touched_id)
});
if should_notify {
ctx.notify();
}
});
There was a problem hiding this comment.
That's good idea - fixed.
|
this looks sweet! |
The footer's history subscription previously called ctx.notify() on every ConversationUsageMetadataUpdated, RemoveConversation, DeletedConversation, and StartedNewConversation event in the app — one terminal view's typing storm would fan out and re-render every other orchestrator's footer. Walk the orchestrator's descendant index and notify only when the touched conversation is the orchestrator itself or one of its locally-known descendants. Drop the StartedNewConversation arm entirely: a freshly spawned descendant always has zero credits, so the rollup result is unchanged until its first ConversationUsageMetadataUpdated event (which this filter then picks up). The walk reads children_by_parent, which is not cleaned up on RemoveConversation/DeletedConversation; just-pruned descendants are still listed, so notify still fires and the render-time rollup correctly drops their row via the loaded-descendants filter. Addresses code review comment from @advait-m on PR #11048. Co-Authored-By: Oz <oz-agent@warp.dev>
- Replace fully-qualified compute_orchestration_rollup() call in render_usage_button with a file-level import. - Add the missing blank line between fn rollup and fn collect_models_by_category in ConversationUsageView. - Extract PER_AGENT_BREAKDOWN_TRUNCATION_CAP for the truncation cap in append_per_agent_rows so the magic 5 isn't repeated. Co-Authored-By: Oz <oz-agent@warp.dev>
Description
Behind the new
FeatureFlag::OrchestrationCreditRollup(dogfood-only) flag, the orchestrator's agent-mode footer now reports the orchestration's combined credit usage — orchestrator plus every locally-loaded descendant child — instead of just the orchestrator's own credits.The expanded footer's "Credits spent (total)" row shows the rollup total and exposes a click-to-expand per-agent breakdown sorted by credits desc (spawn-order tiebreak). The first five rows render inline; any extra rows are revealed by a "Show N more" link. The collapsed pill uses the same rollup total for its headline number and its "has any usage" suppression check; the
(+N)last-block annotation is unchanged. With the flag off, both surfaces render exactly as before.Specs in this PR:
specs/QUALITY-671/PRODUCT.md,specs/QUALITY-671/TECH.md.Mocks: https://www.figma.com/design/AsF5uAM6L5tUmc11vm9YSi/Agent-orchestration?node-id=4636-32697&m=dev



Screenshots:
Key pieces:
app/src/ai/blocklist/usage/rollup.rs— pure aggregation helper. Walks descendants viaBlocklistAIHistoryModel, filters zero-credit contributors, sorts the breakdown, returnsNonewhen nothing applies.app/src/ai/blocklist/orchestration_topology.rs— descendant walker lifted out oforchestration_pill_bar.rsso the pill bar and the rollup share one traversal. The pill bar's relevant tests moved toorchestration_topology_tests.rs; avatar helpers (render_orchestrator_avatar_disc,render_agent_avatar_disc) promoted topub(crate)so the footer rows can reuse them.ConversationUsageView— newnew_footer_with_rollupconstructor subscribes to history events; renders the "View details" / "Hide details" toggle and the per-agent rows. Agent names use the same color as the "USAGE SUMMARY" header; per-agent credit values use the "Credits spent" label color. Names are clipped to the pill bar's max width with ellipsis. Toggles and "Show N more" use the theme's hyperlink color (ansi_fg_blue). A blank value-side placeholder opposite "Show N more" keeps the two-column flex aligned for the rows below.BlocklistAIHistoryEvent::ConversationUsageMetadataUpdated— new conversation-scoped event emitted fromupdate_conversation_cost_and_usage_for_request. The footer view and theAIBlockmodel subscribe so the orchestrator's pill and footer stay live when any descendant spends credits.terminal/view.rs::handle_usage_footer_toggled— routes through the rollup-aware constructor when the flag is enabled, and usesadd_typed_action_viewso the view'sTypedActionViewhandler is actually registered with the framework (without this, the toggle / show-more clicks were silently dropped).output.rs::render_usage_button— collapsed pill computes the rollup behind the flag and feedstotal_creditsinto the headline number + suppression check.Linked Issue
QUALITY-671
Testing
app/src/ai/blocklist/usage/rollup_tests.rs— 8 unit tests covering the aggregation contract: no-descendants → None, sum + sort across descendants, zero-credit filter, transitive grandchildren, spawn-order tie-break, unloaded-descendant skip, six-contributor truncation precondition, all-zero short-circuit.app/src/ai/blocklist/orchestration_topology_tests.rs— tree-walker correctness tests moved over from the pill bar.app/src/ai/blocklist/usage/conversation_usage_view_tests.rs— handler-level tests that stand the view up viaadd_typed_action_viewand verifyToggleDetailsExpanded/ShowAllAgentRowsflip the right state (regression guard against the original "dispatched action has no handlers" bug).cargo fmt --check,cargo clippy --workspace --exclude command-signatures-v2 --all-targets --all-features --tests -- -D warnings, and./script/presubmitall clean../script/runAgent Mode
CHANGELOG-IMPROVEMENT: Roll up child-agent credit usage into the orchestrator's agent-mode footer.
Conversation