diff options
| author | soryu <soryu@soryu.co> | 2026-02-06 20:06:30 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-02-06 20:15:27 +0000 |
| commit | 1b692b8cde4a888c8a35af69231f181b57bf5619 (patch) | |
| tree | 74ce25ce6ee5fb4536b53404e1a0ae923e85c30d /makima/src/daemon/chain/runner.rs | |
| parent | 139be135c2086d725e4f040e744bb25acd436549 (diff) | |
| download | soryu-1b692b8cde4a888c8a35af69231f181b57bf5619.tar.gz soryu-1b692b8cde4a888c8a35af69231f181b57bf5619.zip | |
Fix: Cleanup old chain code
Diffstat (limited to 'makima/src/daemon/chain/runner.rs')
| -rw-r--r-- | makima/src/daemon/chain/runner.rs | 388 |
1 files changed, 0 insertions, 388 deletions
diff --git a/makima/src/daemon/chain/runner.rs b/makima/src/daemon/chain/runner.rs deleted file mode 100644 index 1814581..0000000 --- a/makima/src/daemon/chain/runner.rs +++ /dev/null @@ -1,388 +0,0 @@ -//! Chain runner - creates and orchestrates contracts from chain definitions. -//! -//! Handles the lifecycle of a chain: -//! 1. Parse chain definition -//! 2. Validate DAG -//! 3. Create chain record -//! 4. Create contracts in dependency order -//! 5. Monitor and trigger dependent contracts - -use std::collections::HashMap; -use std::path::Path; -use thiserror::Error; - -use super::dag::{topological_sort, validate_dag, DagError}; -use super::parser::{parse_chain_file, ChainDefinition, ParseError}; -use crate::db::models::{ - AddChainRepositoryRequest, CreateChainContractRequest, CreateChainDeliverableRequest, - CreateChainRequest, CreateChainTaskRequest, -}; - -/// Error type for chain runner operations. -#[derive(Error, Debug)] -pub enum RunnerError { - #[error("Parse error: {0}")] - Parse(#[from] ParseError), - - #[error("DAG error: {0}")] - Dag(#[from] DagError), - - #[error("API error: {0}")] - Api(String), - - #[error("Contract creation failed: {0}")] - ContractCreation(String), -} - -/// Chain runner for creating and managing chains. -pub struct ChainRunner { - /// Base API URL - #[allow(dead_code)] - api_url: String, - /// API key for authentication - #[allow(dead_code)] - api_key: String, -} - -impl ChainRunner { - /// Create a new chain runner. - pub fn new(api_url: String, api_key: String) -> Self { - Self { api_url, api_key } - } - - /// Load and validate a chain from a YAML file. - pub fn load_chain<P: AsRef<Path>>(&self, path: P) -> Result<ChainDefinition, RunnerError> { - let chain = parse_chain_file(path)?; - validate_dag(&chain)?; - Ok(chain) - } - - /// Convert a chain definition to a CreateChainRequest for API submission. - pub fn to_create_request(&self, chain: &ChainDefinition) -> CreateChainRequest { - let contracts: Vec<CreateChainContractRequest> = chain - .contracts - .iter() - .map(|c| CreateChainContractRequest { - name: c.name.clone(), - description: c.description.clone(), - contract_type: Some(c.contract_type.clone()), - initial_phase: None, - phases: c.phases.clone(), - depends_on: c.depends_on.clone(), - tasks: c.tasks.as_ref().map(|tasks| { - tasks - .iter() - .map(|t| CreateChainTaskRequest { - name: t.name.clone(), - plan: t.plan.clone(), - }) - .collect() - }), - deliverables: c.deliverables.as_ref().map(|dels| { - dels.iter() - .map(|d| CreateChainDeliverableRequest { - id: d.id.clone(), - name: d.name.clone(), - priority: Some(d.priority.clone()), - }) - .collect() - }), - editor_x: None, - editor_y: None, - }) - .collect(); - - let (loop_enabled, loop_max_iterations, loop_progress_check) = - match &chain.loop_config { - Some(lc) => ( - Some(lc.enabled), - Some(lc.max_iterations), - lc.progress_check.clone(), - ), - None => (None, None, None), - }; - - // Convert repository definitions to API format - let repositories: Vec<AddChainRepositoryRequest> = chain - .repositories - .iter() - .map(|r| AddChainRepositoryRequest { - name: r.name.clone(), - repository_url: r.repository_url.clone(), - local_path: r.local_path.clone(), - source_type: r.source_type.clone(), - is_primary: r.is_primary, - }) - .collect(); - - CreateChainRequest { - name: chain.name.clone(), - description: chain.description.clone(), - repository_url: None, // Legacy field, repositories take precedence - repositories: if repositories.is_empty() { - None - } else { - Some(repositories) - }, - loop_enabled, - loop_max_iterations, - loop_progress_check, - contracts: Some(contracts), - } - } - - /// Get contracts in topological order (for display/debugging). - pub fn get_execution_order<'a>( - &self, - chain: &'a ChainDefinition, - ) -> Result<Vec<&'a str>, RunnerError> { - Ok(topological_sort(chain)?) - } - - /// Generate ASCII visualization of the chain DAG. - pub fn visualize_dag(&self, chain: &ChainDefinition) -> String { - use super::dag::get_contract_depths; - - let depths = get_contract_depths(chain); - let mut lines: Vec<String> = vec![]; - - lines.push(format!("Chain: {}", chain.name)); - if let Some(desc) = &chain.description { - lines.push(format!(" {}", desc)); - } - lines.push(String::new()); - - // Group contracts by depth - let mut by_depth: HashMap<usize, Vec<&str>> = HashMap::new(); - for contract in &chain.contracts { - let depth = depths.get(contract.name.as_str()).copied().unwrap_or(0); - by_depth.entry(depth).or_default().push(&contract.name); - } - - // Find max depth - let max_depth = by_depth.keys().max().copied().unwrap_or(0); - - // Build visualization - for depth in 0..=max_depth { - if let Some(contracts) = by_depth.get(&depth) { - let contract_strs: Vec<String> = contracts - .iter() - .map(|name| format!("[{}]", name)) - .collect(); - - let indent = " ".repeat(depth); - lines.push(format!("{}{}", indent, contract_strs.join(" "))); - - // Draw arrows to next level - if depth < max_depth { - if let Some(next_contracts) = by_depth.get(&(depth + 1)) { - // Find which contracts connect to the next level - for next in next_contracts { - let next_contract = chain - .contracts - .iter() - .find(|c| c.name.as_str() == *next) - .unwrap(); - - if let Some(deps) = &next_contract.depends_on { - for dep in deps { - if contracts.contains(&dep.as_str()) { - let arrow_indent = " ".repeat(depth); - lines.push(format!("{} │", arrow_indent)); - lines.push(format!("{} ▼", arrow_indent)); - } - } - } - } - } - } - } - } - - lines.join("\n") - } -} - -/// Compute editor positions for contracts based on DAG layout. -/// -/// Returns a map of contract name to (x, y) positions suitable for -/// the GUI editor. -pub fn compute_editor_positions(chain: &ChainDefinition) -> HashMap<String, (f64, f64)> { - use super::dag::get_contract_depths; - - let depths = get_contract_depths(chain); - let mut positions: HashMap<String, (f64, f64)> = HashMap::new(); - - // Group by depth - let mut by_depth: HashMap<usize, Vec<&str>> = HashMap::new(); - for contract in &chain.contracts { - let depth = depths.get(contract.name.as_str()).copied().unwrap_or(0); - by_depth.entry(depth).or_default().push(&contract.name); - } - - // Compute positions: x based on depth, y based on index within depth - let x_spacing = 250.0; - let y_spacing = 150.0; - - for (depth, contracts) in &by_depth { - let x = (*depth as f64) * x_spacing + 100.0; - for (i, name) in contracts.iter().enumerate() { - let y = (i as f64) * y_spacing + 100.0; - positions.insert(name.to_string(), (x, y)); - } - } - - positions -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::daemon::chain::parser::parse_chain_yaml; - - #[test] - fn test_to_create_request() { - let yaml = r#" -name: Test Chain -description: A test chain -repositories: - - name: main - repository_url: https://github.com/test/repo -contracts: - - name: Research - type: simple - phases: [plan, execute] - tasks: - - name: Analyze - plan: "Analyze the codebase" - deliverables: - - id: analysis - name: Analysis Doc - priority: required - - name: Implement - depends_on: [Research] - tasks: - - name: Build - plan: "Build the feature" -loop: - enabled: true - max_iterations: 5 - progress_check: "Check completion" -"#; - let chain = parse_chain_yaml(yaml).unwrap(); - let runner = ChainRunner::new("http://localhost".to_string(), "key".to_string()); - let request = runner.to_create_request(&chain); - - assert_eq!(request.name, "Test Chain"); - assert_eq!(request.description, Some("A test chain".to_string())); - // Repositories are now in a separate array - let repos = request.repositories.unwrap(); - assert_eq!(repos.len(), 1); - assert_eq!( - repos[0].repository_url, - Some("https://github.com/test/repo".to_string()) - ); - assert_eq!(request.loop_enabled, Some(true)); - assert_eq!(request.loop_max_iterations, Some(5)); - - let contracts = request.contracts.unwrap(); - assert_eq!(contracts.len(), 2); - assert_eq!(contracts[0].name, "Research"); - assert_eq!(contracts[0].phases, Some(vec!["plan".to_string(), "execute".to_string()])); - assert_eq!( - contracts[1].depends_on, - Some(vec!["Research".to_string()]) - ); - } - - #[test] - fn test_get_execution_order() { - let yaml = r#" -name: Order Test -contracts: - - name: C - depends_on: [B] - tasks: - - name: Task - plan: "Do C" - - name: A - tasks: - - name: Task - plan: "Do A" - - name: B - depends_on: [A] - tasks: - - name: Task - plan: "Do B" -"#; - let chain = parse_chain_yaml(yaml).unwrap(); - let runner = ChainRunner::new("http://localhost".to_string(), "key".to_string()); - let order = runner.get_execution_order(&chain).unwrap(); - - let pos_a = order.iter().position(|&n| n == "A").unwrap(); - let pos_b = order.iter().position(|&n| n == "B").unwrap(); - let pos_c = order.iter().position(|&n| n == "C").unwrap(); - - assert!(pos_a < pos_b); - assert!(pos_b < pos_c); - } - - #[test] - fn test_visualize_dag() { - let yaml = r#" -name: Visual Test -description: Test visualization -contracts: - - name: A - tasks: - - name: Task - plan: "Do A" - - name: B - depends_on: [A] - tasks: - - name: Task - plan: "Do B" -"#; - let chain = parse_chain_yaml(yaml).unwrap(); - let runner = ChainRunner::new("http://localhost".to_string(), "key".to_string()); - let viz = runner.visualize_dag(&chain); - - assert!(viz.contains("Chain: Visual Test")); - assert!(viz.contains("[A]")); - assert!(viz.contains("[B]")); - } - - #[test] - fn test_compute_editor_positions() { - let yaml = r#" -name: Position Test -contracts: - - name: A - tasks: - - name: Task - plan: "Do A" - - name: B - depends_on: [A] - tasks: - - name: Task - plan: "Do B" - - name: C - depends_on: [A] - tasks: - - name: Task - plan: "Do C" -"#; - let chain = parse_chain_yaml(yaml).unwrap(); - let positions = compute_editor_positions(&chain); - - // A should be at depth 0 (x = 100) - let (a_x, _) = positions.get("A").unwrap(); - assert_eq!(*a_x, 100.0); - - // B and C should be at depth 1 (x = 350) - let (b_x, _) = positions.get("B").unwrap(); - let (c_x, _) = positions.get("C").unwrap(); - assert_eq!(*b_x, 350.0); - assert_eq!(*c_x, 350.0); - } -} |
