diff options
| author | soryu <soryu@soryu.co> | 2026-02-05 23:42:48 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-02-05 23:42:48 +0000 |
| commit | 88a4f15ce1310f8ee8693835be14aa5280233f17 (patch) | |
| tree | 5c1a0417e02071d2198d13478ffa85533b19f891 /makima/src/server/handlers/contract_chat.rs | |
| parent | f1a50b80f3969d150bd1c31edde0aff05369157e (diff) | |
| download | soryu-88a4f15ce1310f8ee8693835be14aa5280233f17.tar.gz soryu-88a4f15ce1310f8ee8693835be14aa5280233f17.zip | |
Add directive-first chain system redesign
Redesigns the chain system with a directive-first architecture where
Directive is the top-level entity (the "why/what") and Chains are
generated execution plans (the "how") that can be dynamically modified.
Backend:
- Add database migration for directive system tables
- Add Directive, DirectiveChain, ChainStep, DirectiveEvent models
- Add DirectiveVerifier and DirectiveApproval models
- Add orchestration module with engine, planner, and verifier
- Add comprehensive API handlers for directives
- Add daemon CLI commands for directive management
- Add directive skill documentation
- Integrate contract completion with directive engine
- Add SSE endpoint for real-time directive events
Frontend:
- Add directives route with split-view layout
- Add 6-tab detail view (Overview, Chain, Events, Evaluations, Approvals, Verifiers)
- Add React Flow DAG visualization for chain steps
- Add SSE subscription hook for real-time event updates
- Add useDirectives and useDirectiveEventSubscription hooks
- Add directive types and API functions
Fixes:
- Fix test failures in ws/protocol, task_output, completion_gate, patch
- Fix word boundary matching in looks_like_task()
- Fix parse_last() to find actual last completion gate
- Fix create_export_patch when merge-base equals HEAD
- Clean up clippy warnings in new code
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'makima/src/server/handlers/contract_chat.rs')
| -rw-r--r-- | makima/src/server/handlers/contract_chat.rs | 1223 |
1 files changed, 18 insertions, 1205 deletions
diff --git a/makima/src/server/handlers/contract_chat.rs b/makima/src/server/handlers/contract_chat.rs index 06b3a7c..8153093 100644 --- a/makima/src/server/handlers/contract_chat.rs +++ b/makima/src/server/handlers/contract_chat.rs @@ -17,8 +17,6 @@ use uuid::Uuid; use crate::db::{ models::{ ContractChatHistoryResponse, ContractWithRelations, CreateTaskRequest, UpdateFileRequest, - AddContractDefinitionRequest, UpdateContractDefinitionRequest, CreateChainRequest, - CreateChainDirectiveRequest, CreateContractEvaluationRequest, }, repository, }; @@ -2767,1211 +2765,26 @@ async fn handle_contract_request( } } - // Chain directive tools - for directive contracts to create and manage chains - ContractToolRequest::CreateChainFromDirective { name, description } => { - // First, get the current contract to verify it's a directive contract - let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await { - Ok(Some(c)) => c, - Ok(None) => return ContractRequestResult { - success: false, - message: "Contract not found".to_string(), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - // Check if contract already has a spawned chain - if contract.spawned_chain_id.is_some() { - return ContractRequestResult { - success: false, - message: "This contract already has a chain associated with it".to_string(), - data: Some(json!({ "existing_chain_id": contract.spawned_chain_id })), - }; - } - - // Create the chain - let chain_req = CreateChainRequest { - name: name.clone(), - description: description.clone(), - repositories: None, - loop_enabled: None, - loop_max_iterations: None, - loop_progress_check: None, - contracts: None, - }; - - let chain = match repository::create_chain_for_owner(pool, owner_id, chain_req).await { - Ok(c) => c, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Failed to create chain: {}", e), - data: None, - }, - }; - - // Link the chain to this directive contract - if let Err(e) = sqlx::query( - r#" - UPDATE chains SET directive_contract_id = $2, evaluation_enabled = true WHERE id = $1; - UPDATE contracts SET spawned_chain_id = $1, is_chain_directive = true WHERE id = $2; - "#, - ) - .bind(chain.id) - .bind(contract_id) - .execute(pool) - .await { - return ContractRequestResult { - success: false, - message: format!("Failed to link chain to contract: {}", e), - data: None, - }; - } - - // Create empty directive for the chain - let directive_req = CreateChainDirectiveRequest { - requirements: Some(vec![]), - acceptance_criteria: Some(vec![]), - constraints: Some(vec![]), - external_dependencies: Some(vec![]), - source_type: Some("llm_generated".to_string()), - }; - - if let Err(e) = repository::create_chain_directive(pool, chain.id, directive_req).await { - return ContractRequestResult { - success: false, - message: format!("Failed to create directive: {}", e), - data: None, - }; - } - - ContractRequestResult { - success: true, - message: format!("Created chain '{}' linked to this directive contract", name), - data: Some(json!({ - "chain_id": chain.id, - "chain_name": name, - "description": description - })), - } - } - - ContractToolRequest::AddChainContract { name, description, contract_type, depends_on, requirement_ids } => { - // Get the contract's spawned chain - let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await { - Ok(Some(c)) => c, - Ok(None) => return ContractRequestResult { - success: false, - message: "Contract not found".to_string(), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - let chain_id = match contract.spawned_chain_id { - Some(id) => id, - None => return ContractRequestResult { - success: false, - message: "This contract has no associated chain. Use create_chain_from_directive first.".to_string(), - data: None, - }, - }; - - // Check for duplicate names - let existing_defs = match repository::list_chain_contract_definitions(pool, chain_id).await { - Ok(defs) => defs, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Failed to list definitions: {}", e), - data: None, - }, - }; - - if existing_defs.iter().any(|d| d.name == name) { - return ContractRequestResult { - success: false, - message: format!("A contract definition with name '{}' already exists", name), - data: None, - }; - } - - // Create the contract definition - let def_req = AddContractDefinitionRequest { - name: name.clone(), - description, - contract_type: contract_type.unwrap_or_else(|| "implementation".to_string()), - initial_phase: Some("research".to_string()), - depends_on, - tasks: None, - deliverables: None, - validation: None, - editor_x: None, - editor_y: None, - }; - - let definition = match repository::create_chain_contract_definition(pool, chain_id, def_req).await { - Ok(d) => d, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Failed to create contract definition: {}", e), - data: None, - }, - }; - - // Update requirement_ids if provided - if let Some(req_ids) = requirement_ids { - if !req_ids.is_empty() { - if let Err(e) = sqlx::query( - "UPDATE chain_contract_definitions SET requirement_ids = $2 WHERE id = $1" - ) - .bind(definition.id) - .bind(&req_ids) - .execute(pool) - .await { - tracing::warn!("Failed to set requirement_ids: {}", e); - } - } - } - - ContractRequestResult { - success: true, - message: format!("Added contract '{}' to chain", name), - data: Some(json!({ - "definition_id": definition.id, - "name": name, - "order_index": definition.order_index - })), - } - } - - ContractToolRequest::SetChainDependencies { contract_name, depends_on } => { - // Get the contract's spawned chain - let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await { - Ok(Some(c)) => c, - Ok(None) => return ContractRequestResult { - success: false, - message: "Contract not found".to_string(), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - let chain_id = match contract.spawned_chain_id { - Some(id) => id, - None => return ContractRequestResult { - success: false, - message: "This contract has no associated chain".to_string(), - data: None, - }, - }; - - // Find the definition by name - let definitions = match repository::list_chain_contract_definitions(pool, chain_id).await { - Ok(defs) => defs, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Failed to list definitions: {}", e), - data: None, - }, - }; - - let definition = match definitions.iter().find(|d| d.name == contract_name) { - Some(d) => d, - None => return ContractRequestResult { - success: false, - message: format!("No contract definition named '{}' found", contract_name), - data: None, - }, - }; - - // Validate that all dependencies exist - for dep_name in &depends_on { - if !definitions.iter().any(|d| &d.name == dep_name) { - return ContractRequestResult { - success: false, - message: format!("Dependency '{}' does not exist", dep_name), - data: None, - }; - } - } - - // Check for circular dependencies (simple check) - if depends_on.contains(&contract_name) { - return ContractRequestResult { - success: false, - message: "A contract cannot depend on itself".to_string(), - data: None, - }; - } - - // Update dependencies - let update_req = UpdateContractDefinitionRequest { - name: None, - description: None, - contract_type: None, - initial_phase: None, - depends_on: Some(depends_on.clone()), - tasks: None, - deliverables: None, - validation: None, - editor_x: None, - editor_y: None, - }; - - match repository::update_chain_contract_definition(pool, definition.id, update_req).await { - Ok(_) => ContractRequestResult { - success: true, - message: format!("Updated dependencies for '{}'", contract_name), - data: Some(json!({ - "contract_name": contract_name, - "depends_on": depends_on - })), - }, - Err(e) => ContractRequestResult { - success: false, - message: format!("Failed to update dependencies: {}", e), - data: None, - }, - } - } - - ContractToolRequest::ModifyChainContract { name, new_name, description, add_requirement_ids, remove_requirement_ids } => { - // Get the contract's spawned chain - let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await { - Ok(Some(c)) => c, - Ok(None) => return ContractRequestResult { - success: false, - message: "Contract not found".to_string(), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - let chain_id = match contract.spawned_chain_id { - Some(id) => id, - None => return ContractRequestResult { - success: false, - message: "This contract has no associated chain".to_string(), - data: None, - }, - }; - - // Find the definition by name - let definitions = match repository::list_chain_contract_definitions(pool, chain_id).await { - Ok(defs) => defs, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Failed to list definitions: {}", e), - data: None, - }, - }; - - let definition = match definitions.iter().find(|d| d.name == name) { - Some(d) => d.clone(), - None => return ContractRequestResult { - success: false, - message: format!("No contract definition named '{}' found", name), - data: None, - }, - }; - - // Check if new name would conflict - if let Some(ref nn) = new_name { - if nn != &name && definitions.iter().any(|d| &d.name == nn) { - return ContractRequestResult { - success: false, - message: format!("A contract definition named '{}' already exists", nn), - data: None, - }; - } - } - - // Update the definition - let update_req = UpdateContractDefinitionRequest { - name: new_name.clone(), - description, - contract_type: None, - initial_phase: None, - depends_on: None, - tasks: None, - deliverables: None, - validation: None, - editor_x: None, - editor_y: None, - }; - - if let Err(e) = repository::update_chain_contract_definition(pool, definition.id, update_req).await { - return ContractRequestResult { - success: false, - message: format!("Failed to update definition: {}", e), - data: None, - }; - } - - // Handle requirement_ids modifications - let mut current_req_ids: Vec<String> = definition.requirement_ids.clone(); - if let Some(add_ids) = add_requirement_ids { - for id in add_ids { - if !current_req_ids.contains(&id) { - current_req_ids.push(id); - } - } - } - if let Some(remove_ids) = remove_requirement_ids { - current_req_ids.retain(|id| !remove_ids.contains(id)); - } - - if current_req_ids != definition.requirement_ids { - if let Err(e) = sqlx::query( - "UPDATE chain_contract_definitions SET requirement_ids = $2 WHERE id = $1" - ) - .bind(definition.id) - .bind(¤t_req_ids) - .execute(pool) - .await { - tracing::warn!("Failed to update requirement_ids: {}", e); - } - } - - ContractRequestResult { - success: true, - message: format!("Modified contract definition '{}'", new_name.as_ref().unwrap_or(&name)), - data: Some(json!({ - "definition_id": definition.id, - "name": new_name.as_ref().unwrap_or(&name), - "requirement_ids": current_req_ids - })), - } - } - - ContractToolRequest::RemoveChainContract { name } => { - // Get the contract's spawned chain - let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await { - Ok(Some(c)) => c, - Ok(None) => return ContractRequestResult { - success: false, - message: "Contract not found".to_string(), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - let chain_id = match contract.spawned_chain_id { - Some(id) => id, - None => return ContractRequestResult { - success: false, - message: "This contract has no associated chain".to_string(), - data: None, - }, - }; - - // Find the definition by name - let definitions = match repository::list_chain_contract_definitions(pool, chain_id).await { - Ok(defs) => defs, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Failed to list definitions: {}", e), - data: None, - }, - }; - - let definition = match definitions.iter().find(|d| d.name == name) { - Some(d) => d, - None => return ContractRequestResult { - success: false, - message: format!("No contract definition named '{}' found", name), - data: None, - }, - }; - - // Check if other definitions depend on this one - let dependents: Vec<&str> = definitions.iter() - .filter(|d| d.depends_on_names.contains(&name)) - .map(|d| d.name.as_str()) - .collect(); - - if !dependents.is_empty() { - return ContractRequestResult { - success: false, - message: format!("Cannot remove '{}': other contracts depend on it: {}", name, dependents.join(", ")), - data: None, - }; - } - - // Delete the definition - match repository::delete_chain_contract_definition(pool, definition.id).await { - Ok(true) => ContractRequestResult { - success: true, - message: format!("Removed contract definition '{}'", name), - data: Some(json!({ "removed": name })), - }, - Ok(false) => ContractRequestResult { - success: false, - message: format!("Failed to remove '{}': not found", name), - data: None, - }, - Err(e) => ContractRequestResult { - success: false, - message: format!("Failed to remove definition: {}", e), - data: None, - }, - } - } - - ContractToolRequest::PreviewChainDag => { - // Get the contract's spawned chain - let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await { - Ok(Some(c)) => c, - Ok(None) => return ContractRequestResult { - success: false, - message: "Contract not found".to_string(), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - let chain_id = match contract.spawned_chain_id { - Some(id) => id, - None => return ContractRequestResult { - success: false, - message: "This contract has no associated chain".to_string(), - data: None, - }, - }; - - // Get chain details and definitions - let chain = match repository::get_chain_for_owner(pool, chain_id, owner_id).await { - Ok(Some(c)) => c, - Ok(None) => return ContractRequestResult { - success: false, - message: "Chain not found".to_string(), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - let definitions = match repository::list_chain_contract_definitions(pool, chain_id).await { - Ok(defs) => defs, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Failed to list definitions: {}", e), - data: None, - }, - }; - - // Build DAG representation - let nodes: Vec<serde_json::Value> = definitions.iter().map(|d| { - json!({ - "name": d.name, - "description": d.description, - "contract_type": d.contract_type, - "depends_on": d.depends_on_names, - "requirement_ids": d.requirement_ids - }) - }).collect(); - - // Build ASCII DAG representation - let mut ascii_dag = String::new(); - ascii_dag.push_str(&format!("Chain: {} ({})\n", chain.name, chain.status)); - ascii_dag.push_str(&format!("Contracts: {}\n\n", definitions.len())); - - // Find root nodes (no dependencies) - let roots: Vec<&str> = definitions.iter() - .filter(|d| d.depends_on_names.is_empty()) - .map(|d| d.name.as_str()) - .collect(); - - ascii_dag.push_str("Root contracts (no dependencies):\n"); - for root in &roots { - ascii_dag.push_str(&format!(" [{}]\n", root)); - } - - ascii_dag.push_str("\nDependency relationships:\n"); - for def in &definitions { - if !def.depends_on_names.is_empty() { - ascii_dag.push_str(&format!(" {} <- {}\n", def.name, def.depends_on_names.join(", "))); - } - } - - ContractRequestResult { - success: true, - message: format!("Chain DAG preview with {} contracts", definitions.len()), - data: Some(json!({ - "chain_id": chain_id, - "chain_name": chain.name, - "chain_status": chain.status, - "contract_count": definitions.len(), - "nodes": nodes, - "ascii_dag": ascii_dag - })), - } - } - - ContractToolRequest::ValidateChainDirective => { - // Get the contract's spawned chain - let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await { - Ok(Some(c)) => c, - Ok(None) => return ContractRequestResult { - success: false, - message: "Contract not found".to_string(), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - let chain_id = match contract.spawned_chain_id { - Some(id) => id, - None => return ContractRequestResult { - success: false, - message: "This contract has no associated chain".to_string(), - data: None, - }, - }; - - let definitions = match repository::list_chain_contract_definitions(pool, chain_id).await { - Ok(defs) => defs, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Failed to list definitions: {}", e), - data: None, - }, - }; - - let mut errors: Vec<String> = Vec::new(); - let mut warnings: Vec<String> = Vec::new(); - - // Check for empty chain - if definitions.is_empty() { - errors.push("Chain has no contract definitions".to_string()); - } - - // Check for circular dependencies - let def_names: std::collections::HashSet<String> = definitions.iter().map(|d| d.name.clone()).collect(); - for def in &definitions { - for dep in &def.depends_on_names { - if !def_names.contains(dep) { - errors.push(format!("'{}' depends on non-existent contract '{}'", def.name, dep)); - } - } - } - - // Simple cycle detection using DFS - fn has_cycle( - name: &str, - definitions: &[crate::db::models::ChainContractDefinition], - visited: &mut std::collections::HashSet<String>, - rec_stack: &mut std::collections::HashSet<String>, - ) -> Option<String> { - visited.insert(name.to_string()); - rec_stack.insert(name.to_string()); - - if let Some(def) = definitions.iter().find(|d| d.name == name) { - for dep in &def.depends_on_names { - if !visited.contains(dep) { - if let Some(cycle) = has_cycle(dep, definitions, visited, rec_stack) { - return Some(cycle); - } - } else if rec_stack.contains(dep) { - return Some(format!("{} -> {}", name, dep)); - } - } - } - - rec_stack.remove(name); - None - } - - let mut visited = std::collections::HashSet::new(); - for def in &definitions { - if !visited.contains(&def.name) { - let mut rec_stack = std::collections::HashSet::new(); - if let Some(cycle) = has_cycle(&def.name, &definitions, &mut visited, &mut rec_stack) { - errors.push(format!("Circular dependency detected: {}", cycle)); - break; - } - } - } - - // Check for orphan contracts (no one depends on them and they're not root) - let roots: std::collections::HashSet<&str> = definitions.iter() - .filter(|d| d.depends_on_names.is_empty()) - .map(|d| d.name.as_str()) - .collect(); - - let depended_on: std::collections::HashSet<&str> = definitions.iter() - .flat_map(|d| d.depends_on_names.iter().map(|s| s.as_str())) - .collect(); - - for def in &definitions { - if !roots.contains(def.name.as_str()) && !depended_on.contains(def.name.as_str()) { - warnings.push(format!("'{}' has dependencies but nothing depends on it (orphan leaf)", def.name)); - } - } - - // Get directive to check requirement coverage - if let Ok(Some(directive)) = repository::get_chain_directive(pool, chain_id).await { - let requirements: Vec<crate::db::models::DirectiveRequirement> = - serde_json::from_value(directive.requirements.clone()).unwrap_or_default(); - - let covered: std::collections::HashSet<&str> = definitions.iter() - .flat_map(|d| d.requirement_ids.iter().map(|s| s.as_str())) - .collect(); - - for req in &requirements { - if !covered.contains(req.id.as_str()) { - warnings.push(format!("Requirement '{}' ({}) is not covered by any contract", req.id, req.title)); - } - } - } - - let is_valid = errors.is_empty(); - - ContractRequestResult { - success: is_valid, - message: if is_valid { - format!("Chain is valid with {} contracts", definitions.len()) - } else { - format!("Chain validation failed with {} errors", errors.len()) - }, - data: Some(json!({ - "valid": is_valid, - "contract_count": definitions.len(), - "errors": errors, - "warnings": warnings - })), - } - } - - ContractToolRequest::FinalizeChainDirective { auto_start } => { - // Get the contract's spawned chain - let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await { - Ok(Some(c)) => c, - Ok(None) => return ContractRequestResult { - success: false, - message: "Contract not found".to_string(), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - let chain_id = match contract.spawned_chain_id { - Some(id) => id, - None => return ContractRequestResult { - success: false, - message: "This contract has no associated chain".to_string(), - data: None, - }, - }; - - // Get chain - let chain = match repository::get_chain_for_owner(pool, chain_id, owner_id).await { - Ok(Some(c)) => c, - Ok(None) => return ContractRequestResult { - success: false, - message: "Chain not found".to_string(), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - if chain.status != "pending" { - return ContractRequestResult { - success: false, - message: format!("Chain is already {} - cannot finalize", chain.status), - data: None, - }; - } - - // Update chain status - let new_status = if auto_start { "active" } else { "pending" }; - if let Err(e) = sqlx::query("UPDATE chains SET status = $2 WHERE id = $1") - .bind(chain_id) - .bind(new_status) - .execute(pool) - .await { - return ContractRequestResult { - success: false, - message: format!("Failed to update chain status: {}", e), - data: None, - }; - } - - // If auto_start, trigger chain progression to create root contracts - if auto_start { - match repository::progress_chain(pool, chain_id, owner_id).await { - Ok(result) => { - ContractRequestResult { - success: true, - message: format!("Chain finalized and started. Created {} root contracts.", result.contracts_created.len()), - data: Some(json!({ - "chain_id": chain_id, - "status": "active", - "contracts_created": result.contracts_created, - "chain_completed": result.chain_completed - })), - } - } - Err(e) => ContractRequestResult { - success: false, - message: format!("Chain finalized but failed to start: {}", e), - data: Some(json!({ "chain_id": chain_id, "status": "active" })), - }, - } - } else { - ContractRequestResult { - success: true, - message: "Chain finalized but not started. Call finalize_chain_directive with auto_start=true to start.".to_string(), - data: Some(json!({ - "chain_id": chain_id, - "status": "pending" - })), - } - } - } - - ContractToolRequest::GetChainStatus => { - // Get the contract's spawned chain - let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await { - Ok(Some(c)) => c, - Ok(None) => return ContractRequestResult { - success: false, - message: "Contract not found".to_string(), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - let chain_id = match contract.spawned_chain_id { - Some(id) => id, - None => return ContractRequestResult { - success: false, - message: "This contract has no associated chain".to_string(), - data: None, - }, - }; - - // Get chain details - let chain = match repository::get_chain_for_owner(pool, chain_id, owner_id).await { - Ok(Some(c)) => c, - Ok(None) => return ContractRequestResult { - success: false, - message: "Chain not found".to_string(), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - // Get definitions - let definitions = match repository::list_chain_contract_definitions(pool, chain_id).await { - Ok(defs) => defs, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Failed to list definitions: {}", e), - data: None, - }, - }; - - // Get instantiated contracts - let chain_contracts = match repository::list_chain_contracts(pool, chain_id).await { - Ok(cc) => cc, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Failed to list chain contracts: {}", e), - data: None, - }, - }; - - // Build status map - let contract_statuses: Vec<serde_json::Value> = chain_contracts.iter().map(|cc| { - json!({ - "name": cc.contract_name, - "contract_id": cc.contract_id, - "status": cc.contract_status, - "phase": cc.contract_phase, - "evaluation_status": cc.evaluation_status, - "evaluation_retry_count": cc.evaluation_retry_count - }) - }).collect(); - - let completed = chain_contracts.iter().filter(|cc| cc.contract_status == "completed").count(); - let active = chain_contracts.iter().filter(|cc| cc.contract_status == "active").count(); - let pending = definitions.len() - chain_contracts.len(); - - ContractRequestResult { - success: true, - message: format!("Chain '{}': {} completed, {} active, {} pending", - chain.name, completed, active, pending), - data: Some(json!({ - "chain_id": chain_id, - "chain_name": chain.name, - "chain_status": chain.status, - "total_definitions": definitions.len(), - "instantiated": chain_contracts.len(), - "completed": completed, - "active": active, - "pending": pending, - "contracts": contract_statuses - })), - } - } - - ContractToolRequest::GetUncoveredRequirements => { - // Get the contract's spawned chain - let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await { - Ok(Some(c)) => c, - Ok(None) => return ContractRequestResult { - success: false, - message: "Contract not found".to_string(), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - let chain_id = match contract.spawned_chain_id { - Some(id) => id, - None => return ContractRequestResult { - success: false, - message: "This contract has no associated chain".to_string(), - data: None, - }, - }; - - // Get directive - let directive = match repository::get_chain_directive(pool, chain_id).await { - Ok(Some(d)) => d, - Ok(None) => return ContractRequestResult { - success: true, - message: "No directive found for this chain".to_string(), - data: Some(json!({ "uncovered": [], "total_requirements": 0 })), - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - // Get definitions - let definitions = match repository::list_chain_contract_definitions(pool, chain_id).await { - Ok(defs) => defs, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Failed to list definitions: {}", e), - data: None, - }, - }; - - // Parse requirements - let requirements: Vec<crate::db::models::DirectiveRequirement> = - serde_json::from_value(directive.requirements.clone()).unwrap_or_default(); - - // Find covered requirement IDs - let covered: std::collections::HashSet<String> = definitions.iter() - .flat_map(|d| d.requirement_ids.iter().cloned()) - .collect(); - - // Find uncovered requirements - let uncovered: Vec<serde_json::Value> = requirements.iter() - .filter(|r| !covered.contains(&r.id)) - .map(|r| json!({ - "id": r.id, - "title": r.title, - "priority": r.priority - })) - .collect(); - - ContractRequestResult { - success: true, - message: format!("{} of {} requirements are uncovered", uncovered.len(), requirements.len()), - data: Some(json!({ - "uncovered": uncovered, - "uncovered_count": uncovered.len(), - "total_requirements": requirements.len(), - "coverage_percent": if requirements.is_empty() { 100.0 } else { - ((requirements.len() - uncovered.len()) as f64 / requirements.len() as f64 * 100.0).round() - } - })), - } - } - - ContractToolRequest::EvaluateContractCompletion { contract_id: target_contract_id, passed, feedback, rework_instructions } => { - // Get the directive contract's spawned chain - let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await { - Ok(Some(c)) => c, - Ok(None) => return ContractRequestResult { - success: false, - message: "Contract not found".to_string(), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - let chain_id = match contract.spawned_chain_id { - Some(id) => id, - None => return ContractRequestResult { - success: false, - message: "This contract has no associated chain".to_string(), - data: None, - }, - }; - - // Verify the target contract is part of this chain - let chain_contract = match repository::get_chain_contract_by_contract_id(pool, target_contract_id).await { - Ok(Some(cc)) => cc, - Ok(None) => return ContractRequestResult { - success: false, - message: format!("Contract {} is not part of a chain", target_contract_id), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - if chain_contract.chain_id != chain_id { - return ContractRequestResult { - success: false, - message: "Contract is not part of this directive's chain".to_string(), - data: None, - }; - } - - // Create evaluation record - let eval_req = CreateContractEvaluationRequest { - contract_id: target_contract_id, - chain_id: Some(chain_id), - chain_contract_id: Some(chain_contract.id), - evaluator_model: Some("directive_contract".to_string()), - passed, - overall_score: if passed { Some(1.0) } else { Some(0.0) }, - criteria_results: vec![], - summary_feedback: feedback.clone(), - rework_instructions: rework_instructions.clone(), - }; - - let evaluation = match repository::create_contract_evaluation(pool, eval_req).await { - Ok(e) => e, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Failed to create evaluation: {}", e), - data: None, - }, - }; - - // Update chain contract evaluation status - let new_status = if passed { "passed" } else { "failed" }; - if let Err(e) = repository::update_chain_contract_evaluation_status( - pool, - chain_contract.id, - new_status, - Some(evaluation.id), - None, // No rework feedback for passed/failed status - ).await { - tracing::warn!("Failed to update chain contract evaluation status: {}", e); - } - - if passed { - // Progress the chain to create downstream contracts - match repository::progress_chain(pool, chain_id, owner_id).await { - Ok(result) => ContractRequestResult { - success: true, - message: format!("Evaluation passed. Created {} downstream contracts.", result.contracts_created.len()), - data: Some(json!({ - "evaluation_id": evaluation.id, - "passed": true, - "contracts_created": result.contracts_created, - "chain_completed": result.chain_completed - })), - }, - Err(e) => ContractRequestResult { - success: true, - message: format!("Evaluation passed but failed to progress chain: {}", e), - data: Some(json!({ - "evaluation_id": evaluation.id, - "passed": true - })), - }, - } - } else { - // Mark contract for rework - if let Err(e) = sqlx::query( - r#" - UPDATE chain_contracts SET evaluation_status = 'rework', rework_feedback = $2 WHERE id = $1; - UPDATE contracts SET status = 'active' WHERE id = (SELECT contract_id FROM chain_contracts WHERE id = $1); - "# - ) - .bind(chain_contract.id) - .bind(&rework_instructions) - .execute(pool) - .await { - tracing::warn!("Failed to mark contract for rework: {}", e); - } - - ContractRequestResult { - success: true, - message: format!("Evaluation failed. Contract marked for rework."), - data: Some(json!({ - "evaluation_id": evaluation.id, - "passed": false, - "rework_instructions": rework_instructions, - "retry_count": chain_contract.evaluation_retry_count + 1 - })), - } - } - } - - ContractToolRequest::RequestRework { contract_id: target_contract_id, feedback } => { - // Get the directive contract's spawned chain - let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await { - Ok(Some(c)) => c, - Ok(None) => return ContractRequestResult { - success: false, - message: "Contract not found".to_string(), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - let chain_id = match contract.spawned_chain_id { - Some(id) => id, - None => return ContractRequestResult { - success: false, - message: "This contract has no associated chain".to_string(), - data: None, - }, - }; - - // Verify the target contract is part of this chain - let chain_contract = match repository::get_chain_contract_by_contract_id(pool, target_contract_id).await { - Ok(Some(cc)) => cc, - Ok(None) => return ContractRequestResult { - success: false, - message: format!("Contract {} is not part of a chain", target_contract_id), - data: None, - }, - Err(e) => return ContractRequestResult { - success: false, - message: format!("Database error: {}", e), - data: None, - }, - }; - - if chain_contract.chain_id != chain_id { - return ContractRequestResult { - success: false, - message: "Contract is not part of this directive's chain".to_string(), - data: None, - }; - } - - // Check retry count - let max_retries = chain_contract.max_evaluation_retries; - if chain_contract.evaluation_retry_count >= max_retries { - return ContractRequestResult { - success: false, - message: format!("Contract has exceeded max retries ({}/{}). Escalate to user.", - chain_contract.evaluation_retry_count, max_retries), - data: Some(json!({ - "retry_count": chain_contract.evaluation_retry_count, - "max_retries": max_retries, - "escalation_required": true - })), - }; - } - - // Mark contract for rework and increment retry count - if let Err(e) = sqlx::query( - r#" - UPDATE chain_contracts - SET evaluation_status = 'rework', - rework_feedback = $2, - evaluation_retry_count = evaluation_retry_count + 1 - WHERE id = $1; - UPDATE contracts SET status = 'active' WHERE id = (SELECT contract_id FROM chain_contracts WHERE id = $1); - "# - ) - .bind(chain_contract.id) - .bind(&feedback) - .execute(pool) - .await { - return ContractRequestResult { - success: false, - message: format!("Failed to request rework: {}", e), - data: None, - }; - } + // Chain directive tools - TEMPORARILY DISABLED + // These tools will be reimplemented using the new directive system. + // See the orchestration module for the new implementation. + ContractToolRequest::CreateChainFromDirective { .. } | + ContractToolRequest::AddChainContract { .. } | + ContractToolRequest::SetChainDependencies { .. } | + ContractToolRequest::ModifyChainContract { .. } | + ContractToolRequest::RemoveChainContract { .. } | + ContractToolRequest::PreviewChainDag | + ContractToolRequest::ValidateChainDirective | + ContractToolRequest::FinalizeChainDirective { .. } | + ContractToolRequest::GetChainStatus | + ContractToolRequest::GetUncoveredRequirements | + ContractToolRequest::EvaluateContractCompletion { .. } | + ContractToolRequest::RequestRework { .. } => { ContractRequestResult { - success: true, - message: format!("Rework requested for contract. Retry {}/{}", - chain_contract.evaluation_retry_count + 1, max_retries), - data: Some(json!({ - "contract_id": target_contract_id, - "retry_count": chain_contract.evaluation_retry_count + 1, - "max_retries": max_retries, - "feedback": feedback - })), + success: false, + message: "Chain directive tools are temporarily disabled. The directive system is being reimplemented.".to_string(), + data: None, } } } |
