From ef03864970bd82307b7bd7e57354164238dc794f Mon Sep 17 00:00:00 2001 From: soryu Date: Mon, 26 Jan 2026 18:39:41 +0000 Subject: Skip automatic completion actions in local_only mode Add `local_only` flag to contracts that prevents automatic completion actions (branch, merge, pr) from executing when tasks complete. This allows users to manually handle code changes via patch files or other means when operating in local-only mode. Changes: - Add `local_only` field to Contract model and request types - Add database migration for the new column - Add `local_only` parameter to SpawnTask command in both state.rs and daemon protocol.rs - Modify task manager to skip completion action execution when `local_only` is true, with appropriate logging - Pass `local_only` flag through all task spawning paths: - mesh_supervisor.rs (task spawn, retry, resume) - mesh.rs (task start, reassign, continue) - mesh_chat.rs (run task) - contract_chat.rs (run task) - Update repository create/update functions to handle `local_only` Co-Authored-By: Claude Opus 4.5 --- makima/src/daemon/task/manager.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'makima/src/daemon/task') diff --git a/makima/src/daemon/task/manager.rs b/makima/src/daemon/task/manager.rs index 74a37bf..b4861d6 100644 --- a/makima/src/daemon/task/manager.rs +++ b/makima/src/daemon/task/manager.rs @@ -973,6 +973,8 @@ pub struct ManagedTask { pub concurrency_key: Uuid, /// Whether to run in autonomous loop mode. pub autonomous_loop: bool, + /// Whether the contract is in local-only mode (skips automatic completion actions). + pub local_only: bool, /// Time task was created. pub created_at: Instant, /// Time task started running. @@ -1670,6 +1672,7 @@ impl TaskManager { conversation_history, patch_data, patch_base_sha, + local_only, } => { tracing::info!( task_id = %task_id, @@ -1696,7 +1699,7 @@ impl TaskManager { parent_task_id, depth, is_orchestrator, is_supervisor, target_repo_path, completion_action, continue_from_task_id, copy_files, contract_id, autonomous_loop, resume_session, - conversation_history, patch_data, patch_base_sha, + conversation_history, patch_data, patch_base_sha, local_only, ).await?; } DaemonCommand::PauseTask { task_id } => { @@ -1774,6 +1777,7 @@ impl TaskManager { let target_repo_path = task.target_repo_path.clone(); let completion_action = task.completion_action.clone(); let contract_id = task.contract_id; + let local_only = task.local_only; // Spawn in background to not block the command handler tokio::spawn(async move { @@ -1796,6 +1800,7 @@ impl TaskManager { None, // conversation_history - not needed for fresh respawn None, // patch_data - not available for respawn None, // patch_base_sha - not available for respawn + local_only, ).await { tracing::error!( task_id = %task_id, @@ -2024,6 +2029,7 @@ impl TaskManager { conversation_history: Option, patch_data: Option, patch_base_sha: Option, + local_only: bool, ) -> TaskResult<()> { tracing::info!(task_id = %task_id, is_orchestrator = is_orchestrator, is_supervisor = is_supervisor, depth = depth, patch_available = patch_data.is_some(), "=== SPAWN_TASK START ==="); @@ -2074,6 +2080,7 @@ impl TaskManager { contract_id, concurrency_key, autonomous_loop, + local_only, created_at: Instant::now(), started_at: None, completed_at: None, @@ -2100,7 +2107,7 @@ impl TaskManager { task_id, task_name, plan, repo_url, base_branch, target_branch, is_orchestrator, is_supervisor, target_repo_path, completion_action, continue_from_task_id, copy_files, contract_id, autonomous_loop, resume_session, - conversation_history, patch_data, patch_base_sha, + conversation_history, patch_data, patch_base_sha, local_only, ).await { tracing::error!(task_id = %task_id, error = %e, "Task execution failed"); inner.mark_failed(task_id, &e.to_string()).await; @@ -3548,6 +3555,7 @@ impl TaskManagerInner { conversation_history: Option, patch_data: Option, patch_base_sha: Option, + local_only: bool, ) -> Result<(), DaemonError> { tracing::info!(task_id = %task_id, is_orchestrator = is_orchestrator, is_supervisor = is_supervisor, resume_session = resume_session, has_patch = patch_data.is_some(), "=== RUN_TASK START ==="); @@ -4627,9 +4635,15 @@ impl TaskManagerInner { } } - // Execute completion action if task succeeded + // Execute completion action if task succeeded (skip in local_only mode) let completion_result = if success { - if let Some(ref action) = completion_action { + if local_only { + tracing::info!( + task_id = %task_id, + "Skipping completion action - contract is in local_only mode" + ); + Ok(None) + } else if let Some(ref action) = completion_action { if action != "none" { self.execute_completion_action( task_id, -- cgit v1.2.3