use regex::Regex;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::agent::Task;
use crate::identity::AgentRole;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TaskEvaluation {
Accept { reason: String },
Delegate {
reason: String,
target_agent: String,
suggestion: String,
},
Clarify {
reason: String,
questions: Vec<String>,
},
Reject { reason: String },
}
#[derive(Debug)]
pub struct TaskBoundaryChecker {
role: AgentRole,
allowed_patterns: Vec<Regex>,
forbidden_patterns: Vec<Regex>,
delegation_targets: HashMap<String, String>,
}
impl TaskBoundaryChecker {
pub fn new(role: AgentRole) -> Self {
let (allowed, forbidden) = Self::create_patterns_for_role(&role);
let delegation_targets = Self::create_delegation_map();
Self {
role,
allowed_patterns: allowed,
forbidden_patterns: forbidden,
delegation_targets,
}
}
pub async fn evaluate_task(&self, task: &Task) -> TaskEvaluation {
if self.is_explicitly_forbidden(task) {
let target_agent = self.determine_correct_agent(task);
return TaskEvaluation::Delegate {
reason: "Task is outside my specialization".to_string(),
target_agent: target_agent.clone(),
suggestion: self.generate_delegation_message(task, &target_agent),
};
}
if self.is_explicitly_allowed(task) {
return TaskEvaluation::Accept {
reason: "Task is within my specialization".to_string(),
};
}
TaskEvaluation::Clarify {
reason: "Task scope is unclear".to_string(),
questions: self.generate_clarification_questions(task),
}
}
fn is_explicitly_allowed(&self, task: &Task) -> bool {
let task_text = format!(
"{} {}",
task.description,
task.details.as_deref().unwrap_or("")
);
self.allowed_patterns
.iter()
.any(|pattern| pattern.is_match(&task_text))
}
fn is_explicitly_forbidden(&self, task: &Task) -> bool {
let task_text = format!(
"{} {}",
task.description,
task.details.as_deref().unwrap_or("")
);
self.forbidden_patterns
.iter()
.any(|pattern| pattern.is_match(&task_text))
}
fn determine_correct_agent(&self, task: &Task) -> String {
let task_text = format!(
"{} {}",
task.description,
task.details.as_deref().unwrap_or("")
);
if self.check_patterns(
&task_text,
&[
r"(?i)(api|backend|server|database|sql|auth|endpoint|jwt)",
r"(?i)(rest|graphql|microservice|grpc|token.*validation)",
],
) {
return self
.delegation_targets
.get("backend")
.cloned()
.unwrap_or_else(|| "backend-agent".to_string());
}
if self.check_patterns(
&task_text,
&[
r"(?i)(ui|frontend|component|react|vue|angular)",
r"(?i)(css|styling|tailwind|sass|layout)",
],
) {
return self
.delegation_targets
.get("frontend")
.cloned()
.unwrap_or_else(|| "frontend-agent".to_string());
}
if self.check_patterns(
&task_text,
&[
r"(?i)(docker|kubernetes|k8s|container)",
r"(?i)(deploy|infrastructure|terraform|aws|gcp|azure)",
r"(?i)(ci/cd|pipeline|jenkins|github.actions)",
],
) {
return self
.delegation_targets
.get("devops")
.cloned()
.unwrap_or_else(|| "devops-agent".to_string());
}
if self.check_patterns(
&task_text,
&[
r"(?i)(test|testing|qa|quality|spec)",
r"(?i)(cypress|jest|playwright|selenium)",
r"(?i)(coverage|automation|e2e|integration)",
],
) {
return self
.delegation_targets
.get("qa")
.cloned()
.unwrap_or_else(|| "qa-agent".to_string());
}
"master-claude".to_string()
}
fn check_patterns(&self, text: &str, patterns: &[&str]) -> bool {
patterns.iter().any(|pattern| {
Regex::new(pattern)
.map(|re| re.is_match(text))
.unwrap_or(false)
})
}
fn generate_delegation_message(&self, task: &Task, target_agent: &str) -> String {
format!(
"Task '{}' appears to be {} work based on the content. \
Recommending delegation to {} for proper handling.",
task.description,
self.categorize_task_type(task),
target_agent
)
}
fn categorize_task_type(&self, task: &Task) -> &str {
let task_text = &task.description.to_lowercase();
if task_text.contains("api") || task_text.contains("backend") {
"backend API"
} else if task_text.contains("ui") || task_text.contains("component") {
"frontend UI"
} else if task_text.contains("deploy") || task_text.contains("infrastructure") {
"DevOps/infrastructure"
} else if task_text.contains("test") || task_text.contains("qa") {
"QA/testing"
} else {
"specialized"
}
}
fn generate_clarification_questions(&self, _task: &Task) -> Vec<String> {
vec![
format!("Is this task specifically related to {}?", self.role.name()),
"What components or systems will this task modify?".to_string(),
"Are there any API, database, or infrastructure changes involved?".to_string(),
format!(
"Should this be handled by a {} specialist?",
self.role.name()
),
]
}
fn create_patterns_for_role(role: &AgentRole) -> (Vec<Regex>, Vec<Regex>) {
match role {
AgentRole::Frontend { .. } => {
let allowed = vec![
r"(?i)(react|vue|angular|svelte)",
r"(?i)(component|jsx|tsx|ui)",
r"(?i)(css|scss|sass|tailwind|styled)",
r"(?i)(frontend|client.?side)",
r"(?i)(state.?management|redux|zustand|mobx)",
r"(?i)(webpack|vite|rollup|parcel)",
r"(?i)(jest.*component|testing.?library)",
];
let forbidden = vec![
r"(?i)(api|endpoint|rest|graphql)",
r"(?i)(database|sql|orm|prisma|typeorm)",
r"(?i)(server|backend|node.*api)",
r"(?i)(docker|kubernetes|terraform)",
r"(?i)(auth.*server|jwt|token.*validation)",
];
(
Self::compile_patterns(allowed),
Self::compile_patterns(forbidden),
)
}
AgentRole::Backend { .. } => {
let allowed = vec![
r"(?i)(api|endpoint|rest|graphql)",
r"(?i)(server|backend|microservice)",
r"(?i)(database|sql|orm|query)",
r"(?i)(auth|jwt|session|oauth)",
r"(?i)(express|fastify|nest|koa)",
r"(?i)(prisma|typeorm|sequelize)",
];
let forbidden = vec![
r"(?i)(react|vue|angular|component)",
r"(?i)(css|scss|tailwind|styling)",
r"(?i)(ui|user.?interface|frontend)",
r"(?i)(docker|kubernetes|helm)",
r"(?i)(terraform|cloudformation)",
];
(
Self::compile_patterns(allowed),
Self::compile_patterns(forbidden),
)
}
AgentRole::DevOps { .. } => {
let allowed = vec![
r"(?i)(docker|container|kubernetes)",
r"(?i)(deploy|deployment|release)",
r"(?i)(ci/cd|pipeline|jenkins)",
r"(?i)(terraform|ansible|cloudformation)",
r"(?i)(aws|gcp|azure|cloud)",
r"(?i)(monitoring|logging|metrics)",
];
let forbidden = vec![
r"(?i)(business.?logic|feature|functionality)",
r"(?i)(component|ui|frontend.*code)",
r"(?i)(api|endpoint|rest|graphql)",
r"(?i)(database.*schema|migration.*create)",
r"(?i)(controller|service|model|repository)",
];
(
Self::compile_patterns(allowed),
Self::compile_patterns(forbidden),
)
}
AgentRole::QA { .. } => {
let allowed = vec![
r"(?i)(test|testing|spec|suite)",
r"(?i)(qa|quality|verification)",
r"(?i)(jest|cypress|playwright|selenium)",
r"(?i)(coverage|automation|e2e)",
r"(?i)(performance.*test|load.*test)",
r"(?i)(security.*test|penetration)",
];
let forbidden = vec![
r"(?i)(implement.*feature|add.*functionality)",
r"(?i)(fix.*bug.*in.*code|patch.*issue)",
r"(?i)(deploy|release|infrastructure)",
r"(?i)(design.*api|create.*endpoint)",
];
(
Self::compile_patterns(allowed),
Self::compile_patterns(forbidden),
)
}
AgentRole::Master { .. } => {
let allowed = vec![
r"(?i)(coordinate|orchestrate|manage)",
r"(?i)(review|quality|standard)",
r"(?i)(architecture|design|planning)",
];
let forbidden = vec![
r"(?i)(implement|code|develop)",
r"(?i)(fix.*bug|patch|hotfix)",
];
(
Self::compile_patterns(allowed),
Self::compile_patterns(forbidden),
)
}
AgentRole::Search { .. } => {
let allowed = vec![
r"(?i)(search|find|lookup|query)",
r"(?i)(information|research|discover)",
r"(?i)(filter|refine|optimize.*query)",
r"(?i)(web.*search|api.*search|gemini)",
r"(?i)(result|source|reference)",
];
let forbidden = vec![
r"(?i)(implement|code|develop)",
r"(?i)(modify|change|update.*file)",
r"(?i)(deploy|release|build)",
r"(?i)(create.*feature|add.*functionality)",
r"(?i)(fix.*bug|patch|refactor)",
];
(
Self::compile_patterns(allowed),
Self::compile_patterns(forbidden),
)
}
}
}
fn compile_patterns(patterns: Vec<&str>) -> Vec<Regex> {
patterns
.into_iter()
.filter_map(|p| Regex::new(p).ok())
.collect()
}
fn create_delegation_map() -> HashMap<String, String> {
let mut map = HashMap::new();
map.insert("frontend".to_string(), "frontend-agent".to_string());
map.insert("backend".to_string(), "backend-agent".to_string());
map.insert("devops".to_string(), "devops-agent".to_string());
map.insert("qa".to_string(), "qa-agent".to_string());
map
}
}