Show orchestrator pill as in-progress while children run#11367
Conversation
The orchestrator's own ConversationStatus flips to Success as soon as its own turn finishes, even while spawned child agents are still running. The orchestration pill (and its hover-card badge) ended up showing either no status or a misleading 'Done' state in that window. Plumb an aggregated child-status accessor (long noted as a TODO in the hover-card path) and feed it to both the orchestrator pill's avatar overlay and the hover card's status badge. Aggregation precedence: InProgress > Blocked > Error > Cancelled > Success, so the pill stays 'In progress' while anything is in flight, surfaces 'Blocked' when a descendant needs user attention, and falls back to the worst-case terminal outcome once everything has finished. Co-Authored-By: Oz <oz-agent@warp.dev>
…e_child comment - Hover card now keeps the child-pill path passing the status by reference (no clone). Orchestrator path holds an aggregated status by value in a deferred-init binding and borrows from it. - Drop redundant type annotations on badge_status / status_badge per WARP.md style. - Correct the is_remote_child comment on the orchestrator pill spec: the flag is a child-placeholder flag and never applies to the orchestrator. Cloud-to-cloud orchestration does exist, but surfacing remote orchestrators via the cloud overlay would need separate plumbing. Co-Authored-By: Oz <oz-agent@warp.dev>
…orchestrator-aggregated-status # Conflicts: # app/src/ai/blocklist/orchestration_topology_tests.rs
|
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 an aggregated orchestration status helper and uses it for the orchestrator pill avatar overlay and hover-card badge. The provided Loom link satisfies the visual-evidence requirement, and I did not find security issues in the changed code.
Concerns
- The aggregation includes the orchestrator conversation's own terminal status, which can surface the unhelpful root
Cancelledstate after all child agents finish successfully.
Verdict
Found: 0 critical, 1 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
| history: &BlocklistAIHistoryModel, | ||
| orchestrator_id: AIConversationId, | ||
| ) -> ConversationStatus { | ||
| let statuses = std::iter::once(orchestrator_id) |
There was a problem hiding this comment.
Cancelled state the UI was hiding: after delegation, once all descendants finish successfully, the root Cancelled will beat Success and the pill will end as Cancelled even though the child tree succeeded. Treat the orchestrator's terminal status specially or aggregate terminal outcomes from descendants only.
There was a problem hiding this comment.
disagree with this - this is intended, if the orchestrator is cancelled then this makes sense
cephalonaut
left a comment
There was a problem hiding this comment.
Sweet! My comments are mostly just about LLM comments - I feel like I'm constantly battling to reduce them to some sane level.
| // the available space (this used to happen on the orchestrator pill, | ||
| // whose title falls back to the conversation's multi-word title rather | ||
| // than a short agent name). | ||
| let name_max_width = HOVER_CARD_WIDTH - 24. - 16. - 8. - 8. - STATUS_BADGE_MAX_WIDTH; |
There was a problem hiding this comment.
Kinda unrelated, but the literals in this calculation should probably be named constants tied to the rendered values. That way we could also trim this massive comment back a bit!
| // the orchestration as a whole. Until we plumb an aggregated | ||
| // child-status accessor we hide the badge for the orchestrator pill | ||
| // — child pills still show the (per-child accurate) badge. | ||
| // The orchestrator's raw `ConversationStatus` only reflects its own |
There was a problem hiding this comment.
Could probably trim much of this
| kind: PillKind::Orchestrator, | ||
| pin_state: PillPinState::Unpinned, | ||
| // Unused: orchestrator pills don't render a status overlay. | ||
| // `is_remote_child` is a child-placeholder flag and never applies |
There was a problem hiding this comment.
Excessive - seems logical that the orchestrator is not a child
| let mut specs = Vec::with_capacity(1 + children.len()); | ||
|
|
||
| // Orchestrator pill first; never pinned (it's the home view). | ||
| // The pill's status badge reflects the orchestration tree as a whole |
There was a problem hiding this comment.
Too verbose? Maybe just a one liner above line 620: Orchestrator badge is aggregated with children's statuses?
| let leading: Box<dyn Element> = match kind { | ||
| PillKind::Orchestrator => match status.as_ref() { | ||
| // Defensive: orchestrator pills don't currently carry a status. | ||
| // Orchestrator pills always carry an aggregated status (see |
There was a problem hiding this comment.
Just drop this comment
haha yep - thanks for calling these out, fixing |
…orchestrator-aggregated-status # Conflicts: # app/src/ai/blocklist/orchestration_topology_tests.rs Co-Authored-By: Oz <oz-agent@warp.dev>
Loom: https://www.loom.com/share/5011222654f54c348f5f460632abfd5e
Description
Closes QUALITY-718.
The orchestrator's own
ConversationStatusflips toSuccessas soon as its own streaming turn finishes, even while spawned child agents are still running. That left the orchestration pill (and its hover-card badge) showing no status overlay at all — and the long-standing comment inrender_hover_card("Until we plumb an aggregated child-status accessor we hide the badge for the orchestrator pill") explicitly punted on it.This PR plumbs the aggregated child-status accessor and feeds it to both the orchestrator pill's avatar overlay and the hover-card badge.
Aggregation precedence (highest wins):
InProgress— any node in the tree is actively runningBlocked— at least one node is waiting on user input (preserves the firstblocked_actionpayload)Error— at least one node finished with an errorCancelled— at least one node was cancelledSuccess— everything finished successfullyThis means the orchestrator pill now stays "In progress" while any descendant is still running, surfaces "Blocked" when a descendant needs user attention, and falls back to the worst-case terminal outcome once everything has finished. Per design feedback: if anyone is still in-progress we never flip the orchestrator to a terminal status.
Testing
orchestration_topology_tests.rscovering each precedence rule (in-progress wins over errored siblings; blocked wins over terminal states; error > cancelled > success; orchestrator's ownInProgressis respected; all-Successcase).cargo fmtclean.cargo clippy/cargo nextestwere not run locally — my Xcode is missing the Metal Toolchain sowarpui'sbuild.rsfails before reaching Rust code. Relying on CI for clippy + the unit tests above.