diff options
| author | soryu <soryu@soryu.co> | 2026-02-04 01:07:14 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-02-04 01:07:14 +0000 |
| commit | a734bf1a472b19d63341769d26a66628575df7f4 (patch) | |
| tree | ec78f57e5721d157c620df0c99de5b5efe485231 /makima/src/server | |
| parent | c732dd048128808cd9f67f6e1176a5b565df5678 (diff) | |
| download | soryu-a734bf1a472b19d63341769d26a66628575df7f4.tar.gz soryu-a734bf1a472b19d63341769d26a66628575df7f4.zip | |
Add chain checkpoint contracts
Diffstat (limited to 'makima/src/server')
| -rw-r--r-- | makima/src/server/handlers/chains.rs | 150 | ||||
| -rw-r--r-- | makima/src/server/handlers/contracts.rs | 29 |
2 files changed, 170 insertions, 9 deletions
diff --git a/makima/src/server/handlers/chains.rs b/makima/src/server/handlers/chains.rs index 5d26e6a..ae19ca0 100644 --- a/makima/src/server/handlers/chains.rs +++ b/makima/src/server/handlers/chains.rs @@ -15,8 +15,8 @@ use uuid::Uuid; use crate::db::models::{ AddContractDefinitionRequest, ChainContractDefinition, ChainContractDetail, ChainDefinitionGraphResponse, ChainEditorData, ChainEvent, ChainGraphResponse, ChainSummary, - ChainWithContracts, CreateChainRequest, StartChainResponse, UpdateChainRequest, - UpdateContractDefinitionRequest, + ChainWithContracts, CreateChainRequest, CreateTaskRequest, StartChainRequest, + StartChainResponse, UpdateChainRequest, UpdateContractDefinitionRequest, }; use crate::db::repository::{self, RepositoryError}; use crate::server::auth::Authenticated; @@ -1048,6 +1048,7 @@ pub async fn get_chain_definition_graph( params( ("id" = Uuid, Path, description = "Chain ID") ), + request_body(content = Option<StartChainRequest>, description = "Optional start options"), responses( (status = 200, description = "Chain started", body = StartChainResponse), (status = 400, description = "Chain cannot be started", body = ApiError), @@ -1066,6 +1067,7 @@ pub async fn start_chain( State(state): State<SharedState>, Authenticated(auth): Authenticated, Path(chain_id): Path<Uuid>, + body: Option<Json<StartChainRequest>>, ) -> impl IntoResponse { let Some(ref pool) = state.db_pool else { return ( @@ -1075,6 +1077,11 @@ pub async fn start_chain( .into_response(); }; + let req = body.map(|b| b.0).unwrap_or(StartChainRequest { + with_supervisor: false, + repository_url: None, + }); + // Verify ownership and get chain let chain = match repository::get_chain_for_owner(pool, chain_id, auth.owner_id).await { Ok(Some(c)) => c, @@ -1132,8 +1139,7 @@ pub async fn start_chain( .into_response(); } - // TODO: Implement chain supervisor spawning - // For now, just update the chain status to active + // Update chain status to active match repository::update_chain_status(pool, chain_id, "active").await { Ok(_) => {} Err(e) => { @@ -1146,13 +1152,139 @@ pub async fn start_chain( } } - // Return response indicating chain has started - // supervisor_task_id is None until we implement the supervisor daemon + // Create supervisor task if requested + let mut supervisor_task_id: Option<Uuid> = None; + if req.with_supervisor { + let supervisor_name = format!("Chain Supervisor: {}", chain.name); + let supervisor_plan = format!( + r#"You are the supervisor for chain "{}". + +## Environment Variables +- MAKIMA_CHAIN_ID={} +- MAKIMA_API_URL (configured) +- MAKIMA_API_KEY (configured) + +## Your Responsibilities +1. Monitor chain progress by periodically checking chain status +2. Validate that contracts are completing successfully +3. Identify and report any issues or blockers +4. Track the overall chain progress through the DAG + +## Available Commands +Use these makima CLI commands to monitor the chain: + +```bash +# Check chain status +makima chain status {} + +# List contracts in the chain +makima chain contracts {} + +# View the chain DAG with current status +makima chain graph {} --with-status +``` + +## Monitoring Loop +1. Check chain status every few minutes +2. If a contract fails, investigate the issue +3. Report progress to the user when milestones are reached +4. Mark the chain as complete when all contracts finish + +## Current Chain Info +- Chain ID: {} +- Chain Name: {} +- Total definitions: {} + +Begin monitoring the chain. Check the initial status and report what you find."#, + chain.name, + chain_id, + chain_id, + chain_id, + chain_id, + chain_id, + chain.name, + definitions.len() + ); + + let supervisor_req = CreateTaskRequest { + name: supervisor_name, + description: Some(format!("Supervisor task for chain: {}", chain.name)), + plan: supervisor_plan, + repository_url: req.repository_url.clone(), + base_branch: None, + target_branch: None, + parent_task_id: None, + contract_id: None, // Chain supervisor is not tied to a specific contract + target_repo_path: None, + completion_action: None, + continue_from_task_id: None, + copy_files: None, + is_supervisor: true, + checkpoint_sha: None, + priority: 0, + merge_mode: None, + branched_from_task_id: None, + conversation_history: None, + supervisor_worktree_task_id: None, + }; + + match repository::create_task_for_owner(pool, auth.owner_id, supervisor_req).await { + Ok(supervisor_task) => { + tracing::info!( + chain_id = %chain_id, + supervisor_task_id = %supervisor_task.id, + "Created supervisor task for chain" + ); + + // Update chain with supervisor_task_id + if let Err(e) = + repository::set_chain_supervisor_task(pool, chain_id, Some(supervisor_task.id)) + .await + { + tracing::warn!( + chain_id = %chain_id, + error = %e, + "Failed to link supervisor task to chain" + ); + } + + supervisor_task_id = Some(supervisor_task.id); + } + Err(e) => { + tracing::warn!( + chain_id = %chain_id, + error = %e, + "Failed to create supervisor task for chain" + ); + } + } + } + + // Progress the chain - this creates root contracts (definitions with no dependencies) + let progression = match repository::progress_chain(pool, chain_id, auth.owner_id).await { + Ok(p) => p, + Err(e) => { + tracing::error!("Failed to progress chain: {}", e); + // Chain is active but no contracts created - return partial success + return Json(StartChainResponse { + chain_id, + supervisor_task_id, + contracts_created: vec![], + status: "active".to_string(), + }) + .into_response(); + } + }; + Json(StartChainResponse { chain_id, - supervisor_task_id: None, - contracts_created: vec![], - status: "started".to_string(), + supervisor_task_id, + contracts_created: progression.contracts_created, + status: if progression.chain_completed { + "completed".to_string() + } else { + "active".to_string() + }, }) .into_response() } diff --git a/makima/src/server/handlers/contracts.rs b/makima/src/server/handlers/contracts.rs index 8c8cabf..54bae71 100644 --- a/makima/src/server/handlers/contracts.rs +++ b/makima/src/server/handlers/contracts.rs @@ -574,6 +574,35 @@ pub async fn update_contract( "status": &contract.status, }), ).await; + + // If contract is part of a chain, progress the chain + if let Some(chain_id) = contract.chain_id { + let pool_clone = pool.clone(); + let owner_id = auth.owner_id; + tokio::spawn(async move { + match repository::progress_chain(&pool_clone, chain_id, owner_id).await { + Ok(result) => { + if !result.contracts_created.is_empty() { + tracing::info!( + chain_id = %chain_id, + contracts_created = ?result.contracts_created, + "Chain progressed - created new contracts" + ); + } + if result.chain_completed { + tracing::info!(chain_id = %chain_id, "Chain completed"); + } + } + Err(e) => { + tracing::error!( + chain_id = %chain_id, + error = %e, + "Failed to progress chain after contract completion" + ); + } + } + }); + } } // Get summary with counts |
