Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Batch fix covering multiple modules:
- P2-01: HandRegistry Semaphore-based max_concurrent enforcement
- P2-03: Populate toolCount/metricCount from Hand trait methods
- P2-06: heartbeat_update_config minimum interval validation
- P2-07: ReflectionResult used_fallback marker for rule-based fallback
- P2-08/09: identity_propose_change parameter naming consistency
- P2-10: ClassroomMetadata is_placeholder flag for LLM failure
- P2-11: classroomStore userDidCloseDuringGeneration intent tracking
- P2-12: workflowStore pipeline_create sends actionType
- P2-13/14: PipelineInfo step_count + PipelineStepInfo for proper step mapping
- P2-15: Pipe transform support in context.resolve (8 transforms)
- P2-16: Mustache {{...}} → \${...} auto-normalization
- P2-17: SaaSLogin password placeholder 6→8
- P2-19: serialize_skill_md + update_skill preserve tools field
- P2-22: ToolOutputGuard sensitive patterns from warn→block
- P2-23: Mutex::unwrap() → unwrap_or_else in relay/service.rs
- P3-01/03/07/08/09: Various P3 fixes
- DEFECT_LIST.md: comprehensive status sync (43/51 fixed, 8 remaining)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
112 lines
3.4 KiB
Rust
112 lines
3.4 KiB
Rust
//! Hand and Trigger registries
|
|
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
use tokio::sync::{RwLock, Semaphore};
|
|
use zclaw_types::Result;
|
|
|
|
use super::{Hand, HandConfig, HandContext, HandResult};
|
|
|
|
/// Hand registry with per-hand concurrency control (P2-01)
|
|
pub struct HandRegistry {
|
|
hands: RwLock<HashMap<String, Arc<dyn Hand>>>,
|
|
configs: RwLock<HashMap<String, HandConfig>>,
|
|
/// Per-hand semaphores for max_concurrent enforcement (key: hand id)
|
|
semaphores: RwLock<HashMap<String, Arc<Semaphore>>>,
|
|
}
|
|
|
|
impl HandRegistry {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
hands: RwLock::new(HashMap::new()),
|
|
configs: RwLock::new(HashMap::new()),
|
|
semaphores: RwLock::new(HashMap::new()),
|
|
}
|
|
}
|
|
|
|
/// Register a hand
|
|
pub async fn register(&self, hand: Arc<dyn Hand>) {
|
|
let config = hand.config().clone();
|
|
let mut hands = self.hands.write().await;
|
|
let mut configs = self.configs.write().await;
|
|
|
|
// P2-01: Create semaphore for max_concurrent enforcement
|
|
if config.max_concurrent > 0 {
|
|
let mut semaphores = self.semaphores.write().await;
|
|
semaphores.insert(
|
|
config.id.clone(),
|
|
Arc::new(Semaphore::new(config.max_concurrent as usize)),
|
|
);
|
|
}
|
|
|
|
hands.insert(config.id.clone(), hand);
|
|
configs.insert(config.id.clone(), config);
|
|
}
|
|
|
|
/// Get a hand by ID
|
|
pub async fn get(&self, id: &str) -> Option<Arc<dyn Hand>> {
|
|
let hands = self.hands.read().await;
|
|
hands.get(id).cloned()
|
|
}
|
|
|
|
/// Get hand configuration
|
|
pub async fn get_config(&self, id: &str) -> Option<HandConfig> {
|
|
let configs = self.configs.read().await;
|
|
configs.get(id).cloned()
|
|
}
|
|
|
|
/// List all hands
|
|
pub async fn list(&self) -> Vec<HandConfig> {
|
|
let configs = self.configs.read().await;
|
|
configs.values().cloned().collect()
|
|
}
|
|
|
|
/// Execute a hand with concurrency limiting (P2-01)
|
|
pub async fn execute(
|
|
&self,
|
|
id: &str,
|
|
context: &HandContext,
|
|
input: serde_json::Value,
|
|
) -> Result<HandResult> {
|
|
let hand = self.get(id).await
|
|
.ok_or_else(|| zclaw_types::ZclawError::NotFound(format!("Hand not found: {}", id)))?;
|
|
|
|
// P2-01: Acquire semaphore permit if max_concurrent is set
|
|
let semaphore_opt = {
|
|
let semaphores = self.semaphores.read().await;
|
|
semaphores.get(id).cloned()
|
|
};
|
|
|
|
if let Some(semaphore) = semaphore_opt {
|
|
let _permit = semaphore.acquire().await
|
|
.map_err(|_| zclaw_types::ZclawError::Internal(
|
|
format!("Hand '{}' semaphore closed", id)
|
|
))?;
|
|
hand.execute(context, input).await
|
|
} else {
|
|
hand.execute(context, input).await
|
|
}
|
|
}
|
|
|
|
/// Remove a hand
|
|
pub async fn remove(&self, id: &str) {
|
|
let mut hands = self.hands.write().await;
|
|
let mut configs = self.configs.write().await;
|
|
let mut semaphores = self.semaphores.write().await;
|
|
hands.remove(id);
|
|
configs.remove(id);
|
|
semaphores.remove(id);
|
|
}
|
|
|
|
/// P2-03: Get tool and metric counts for a hand
|
|
pub async fn get_counts(&self, id: &str) -> (u32, u32) {
|
|
let hands = self.hands.read().await;
|
|
if let Some(hand) = hands.get(id) {
|
|
(hand.tool_count(), hand.metric_count())
|
|
} else {
|
|
(0, 0)
|
|
}
|
|
}
|
|
}
|
|
|