summaryrefslogtreecommitdiff
path: root/makima/src/server/handlers/contract_chat.rs
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-05 23:42:48 +0000
committersoryu <soryu@soryu.co>2026-02-05 23:42:48 +0000
commit88a4f15ce1310f8ee8693835be14aa5280233f17 (patch)
tree5c1a0417e02071d2198d13478ffa85533b19f891 /makima/src/server/handlers/contract_chat.rs
parentf1a50b80f3969d150bd1c31edde0aff05369157e (diff)
downloadsoryu-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.rs1223
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(&current_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,
}
}
}