huskies: merge 994

This commit is contained in:
dave
2026-05-13 22:34:15 +00:00
parent 1ee23e7bfe
commit a5cd3a2152
8 changed files with 121 additions and 26 deletions
+63 -1
View File
@@ -6,7 +6,23 @@ use std::collections::HashMap;
use super::super::super::{AgentStatus, PipelineStage, agent_config_stage, pipeline_stage};
use super::super::StoryAgent;
/// Return `true` if the agent has a throttle set whose expiry has already passed.
///
/// Returns `false` when the agent has no throttle, or when the throttle's
/// `until` time is still in the future (throttle is active, agent is waiting).
fn is_throttle_expired(agent: &StoryAgent) -> bool {
agent
.throttled
.as_ref()
.map(|e| !e.is_active())
.unwrap_or(false)
}
/// Return `true` if `agent_name` has no active (pending/running) entry in the pool.
///
/// An agent with an expired throttle is considered free even if its status
/// is still `Running` — the scheduler may retry rather than skip indefinitely.
/// Agents without any throttle (or with an active throttle) are still considered busy.
pub(in crate::agents::pool) fn is_agent_free(
agents: &HashMap<String, StoryAgent>,
agent_name: &str,
@@ -14,6 +30,7 @@ pub(in crate::agents::pool) fn is_agent_free(
!agents.values().any(|a| {
a.agent_name == agent_name
&& matches!(a.status, AgentStatus::Running | AgentStatus::Pending)
&& !is_throttle_expired(a)
})
}
@@ -148,7 +165,7 @@ mod tests {
project_root: None,
log_session_id: None,
merge_failure_reported: false,
throttled: false,
throttled: None,
termination_reason: None,
status_buffer: None,
}
@@ -569,6 +586,51 @@ model = "sonnet"
assert_eq!(free, Some("qa"));
}
// ── is_agent_free: throttle-expiry behaviour ─────────────────────────
#[test]
fn is_agent_free_returns_false_for_running_agent_no_throttle() {
let mut agents = HashMap::new();
agents.insert(
"s1:coder-1".to_string(),
make_test_story_agent("coder-1", AgentStatus::Running),
);
assert!(
!is_agent_free(&agents, "coder-1"),
"running agent with no throttle should be busy"
);
}
#[test]
fn is_agent_free_returns_false_for_running_agent_with_active_throttle() {
let mut agent = make_test_story_agent("coder-1", AgentStatus::Running);
// Throttle expires far in the future → still active.
agent.throttled = Some(crate::agents::AgentExecution::Throttled {
until: chrono::Utc::now() + chrono::Duration::hours(1),
});
let mut agents = HashMap::new();
agents.insert("s1:coder-1".to_string(), agent);
assert!(
!is_agent_free(&agents, "coder-1"),
"running agent with active throttle should still be busy"
);
}
#[test]
fn is_agent_free_returns_true_for_running_agent_with_expired_throttle() {
let mut agent = make_test_story_agent("coder-1", AgentStatus::Running);
// Throttle expired in the past → agent is eligible for retry.
agent.throttled = Some(crate::agents::AgentExecution::Throttled {
until: chrono::Utc::now() - chrono::Duration::minutes(1),
});
let mut agents = HashMap::new();
agents.insert("s1:coder-1".to_string(), agent);
assert!(
is_agent_free(&agents, "coder-1"),
"running agent with expired throttle should be considered free"
);
}
// ── count_active_agents_for_stage ────────────────────────────────────
#[test]