Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions app/src/ai/blocklist/inline_action/run_agents_card_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1391,13 +1391,19 @@ pub(crate) fn format_terminal_state(result: &RunAgentsResult) -> (String, Status
}
}
RunAgentsResult::Denied { reason } => {
let body = if reason.is_empty() {
"Orchestration is currently disabled. Re-enable on the plan card to launch."
.to_string()
// A denial is not the same as orchestration being disabled. The
// request can be declined while orchestration is fully enabled —
// most commonly by the duplicate-launch guard (the agent re-ran
// run_agents for children that were already spawned). Surface the
// actual reason instead of always claiming orchestration is off,
// which previously misled users whose agents were merely duplicates
// and pointed them at a "plan card" re-enable affordance that the
// terminal card doesn't have.
let body = if reason.trim().is_empty() {
// An empty reason is the user's "Accept w/o orchestration" choice.
"Continued without orchestration.".to_string()
} else {
format!(
"Orchestration is currently disabled. Re-enable on the plan card to launch. ({reason})"
)
reason.clone()
};
(body, StatusKind::Cancelled)
}
Expand Down
30 changes: 25 additions & 5 deletions app/src/ai/blocklist/inline_action/run_agents_card_view_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,20 +347,40 @@ mod format_terminal_state_tests {
}

#[test]
fn denied_with_reason_appends_reason() {
fn denied_with_reason_surfaces_reason() {
let (label, kind) = format_terminal_state(&RunAgentsResult::Denied {
reason: "disapproved".to_string(),
reason: "Orchestration config was disapproved".to_string(),
});
assert!(label.contains("disapproved"));
assert_eq!(label, "Orchestration config was disapproved");
// A denial must not falsely claim orchestration is disabled.
assert!(!label.contains("Orchestration is currently disabled"));
assert!(matches!(kind, StatusKind::Cancelled));
}

#[test]
fn denied_without_reason_uses_short_label() {
fn denied_with_duplicate_launch_reason_does_not_claim_disabled() {
// Regression: the duplicate-launch guard denies a run_agents call while
// orchestration is fully enabled (the agent re-ran run_agents for
// children that were already spawned). The card must surface that
// reason, not the misleading "Orchestration is currently disabled" copy.
let reason = "Requested agent(s) have already been launched: frontend (a-1). \
Do not start duplicate child agents; send any follow-up with \
send_message_to_agent using the existing agent id(s): a-1.";
let (label, kind) = format_terminal_state(&RunAgentsResult::Denied {
reason: reason.to_string(),
});
assert_eq!(label, reason);
assert!(!label.contains("Orchestration is currently disabled"));
assert!(matches!(kind, StatusKind::Cancelled));
}

#[test]
fn denied_without_reason_reads_as_deliberate_skip() {
let (label, kind) = format_terminal_state(&RunAgentsResult::Denied {
reason: String::new(),
});
assert!(!label.contains("()"));
assert_eq!(label, "Continued without orchestration.");
assert!(!label.contains("Orchestration is currently disabled"));
assert!(matches!(kind, StatusKind::Cancelled));
}

Expand Down
Loading