fix: agent capability matching in dispatch — only agent: labels are requirements

Previous bug: only code:* and review labels were checked, so agent:document,
agent:tests etc. were never filtered. Any agent could pick up any task.

Now: labels with agent: prefix are matched against agent capabilities.
Other labels are treated as metadata. Includes regression test.
This commit is contained in:
Zer4tul 2026-05-12 23:51:08 +08:00
parent 1f351a1734
commit a18cb2824e
6 changed files with 1271 additions and 8 deletions

View file

@ -92,8 +92,11 @@ impl Dispatcher {
for host in &self.config.hosts {
for agent in &host.agents {
let supports_caps = task.labels.iter().all(|label| {
!label.starts_with("code:") && !label.starts_with("review")
|| agent.capabilities.iter().any(|cap| cap == label)
if let Some(cap) = label.strip_prefix("agent:") {
agent.capabilities.iter().any(|agent_cap| agent_cap == cap || agent_cap == label)
} else {
true
}
});
if !supports_caps {
continue;
@ -211,4 +214,19 @@ mod tests {
let selected = dispatcher.select_host(&sample_task()).await.unwrap().unwrap();
assert_eq!(selected.0.host_id, "h2");
}
#[tokio::test]
async fn does_not_match_agent_label_without_capability() {
let dir = TempDir::new().unwrap();
let db = dir.path().join("test.db");
let store = Arc::new(Mutex::new(EventStore::open(&db).unwrap()));
let sm = Arc::new(StateMachine::new(store.clone()));
let dispatcher = Dispatcher::new(config(), store, sm);
let mut task = sample_task();
task.labels = vec!["agent:document".into(), "priority:urgent".into()];
let selected = dispatcher.select_host(&task).await.unwrap();
assert!(selected.is_none());
}
}