summaryrefslogtreecommitdiff
path: root/makima/src/server/handlers
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-30 03:28:40 +0000
committersoryu <soryu@soryu.co>2026-01-30 03:28:40 +0000
commit4c09aa13a50064a4145ef53021490d303e46bc5e (patch)
treed1fa901d97e54a7840e5ac153270bdab66bb8c99 /makima/src/server/handlers
parentdac1adb138f532245a36fa16524f1e4fb9990173 (diff)
downloadsoryu-makima/auto-merge-local.tar.gz
soryu-makima/auto-merge-local.zip
Add auto_merge_local option for local-only contractsmakima/auto-merge-local
When local_only=true on a contract, all completion actions are skipped. This adds a new option auto_merge_local that, when enabled along with local_only, will automatically merge completed task changes to the master/main branch locally (without pushing or creating PRs). Changes: - Add auto_merge_local column to contracts table (migration) - Add auto_merge_local field to Contract model and summary - Update CreateContractRequest and UpdateContractRequest structs - Update contract repository create/update functions - Add auto_merge_local to WebSocket protocol StartTask command - Pass auto_merge_local through spawn_task and run_task functions - Modify task manager completion logic: if local_only=true AND auto_merge_local=true, execute 'merge' completion action locally - Update all server handlers to retrieve and pass auto_merge_local - Add TypeScript types to frontend components Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'makima/src/server/handlers')
-rw-r--r--makima/src/server/handlers/contract_chat.rs12
-rw-r--r--makima/src/server/handlers/contracts.rs4
-rw-r--r--makima/src/server/handlers/mesh.rs46
-rw-r--r--makima/src/server/handlers/mesh_chat.rs11
-rw-r--r--makima/src/server/handlers/mesh_supervisor.rs4
-rw-r--r--makima/src/server/handlers/transcript_analysis.rs1
6 files changed, 48 insertions, 30 deletions
diff --git a/makima/src/server/handlers/contract_chat.rs b/makima/src/server/handlers/contract_chat.rs
index a066595..b5255f5 100644
--- a/makima/src/server/handlers/contract_chat.rs
+++ b/makima/src/server/handlers/contract_chat.rs
@@ -1571,14 +1571,14 @@ async fn handle_contract_request(
}
};
- // Get local_only from contract if task has one
- let local_only = if let Some(contract_id) = task.contract_id {
+ // Get local_only and auto_merge_local from contract if task has one
+ let (local_only, auto_merge_local) = if let Some(contract_id) = task.contract_id {
match repository::get_contract_for_owner(pool, contract_id, owner_id).await {
- Ok(Some(contract)) => contract.local_only,
- _ => false,
+ Ok(Some(contract)) => (contract.local_only, contract.auto_merge_local),
+ _ => (false, false),
}
} else {
- false
+ (false, false)
};
// Send SpawnTask command to daemon
@@ -1604,6 +1604,7 @@ async fn handle_contract_request(
patch_data: None,
patch_base_sha: None,
local_only,
+ auto_merge_local,
supervisor_worktree_task_id: None, // Not spawned by supervisor
};
@@ -2593,6 +2594,7 @@ async fn handle_contract_request(
autonomous_loop: None,
phase_guard: None,
local_only: None,
+ auto_merge_local: None,
red_team_enabled: None,
red_team_prompt: None,
template_id: None,
diff --git a/makima/src/server/handlers/contracts.rs b/makima/src/server/handlers/contracts.rs
index 9979c1f..6c237dc 100644
--- a/makima/src/server/handlers/contracts.rs
+++ b/makima/src/server/handlers/contracts.rs
@@ -369,6 +369,7 @@ pub async fn create_contract(
status: contract.status,
supervisor_task_id: contract.supervisor_task_id,
local_only: contract.local_only,
+ auto_merge_local: contract.auto_merge_local,
red_team_enabled: contract.red_team_enabled,
file_count: 0,
task_count: 0,
@@ -392,6 +393,7 @@ pub async fn create_contract(
status: contract.status,
supervisor_task_id: contract.supervisor_task_id,
local_only: contract.local_only,
+ auto_merge_local: contract.auto_merge_local,
red_team_enabled: contract.red_team_enabled,
file_count: 0,
task_count: 0,
@@ -522,6 +524,7 @@ pub async fn update_contract(
status: contract.status,
supervisor_task_id: contract.supervisor_task_id,
local_only: contract.local_only,
+ auto_merge_local: contract.auto_merge_local,
red_team_enabled: contract.red_team_enabled,
file_count: 0,
task_count: 0,
@@ -1408,6 +1411,7 @@ pub async fn change_phase(
status: updated_contract.status,
supervisor_task_id: updated_contract.supervisor_task_id,
local_only: updated_contract.local_only,
+ auto_merge_local: updated_contract.auto_merge_local,
red_team_enabled: updated_contract.red_team_enabled,
file_count: 0,
task_count: 0,
diff --git a/makima/src/server/handlers/mesh.rs b/makima/src/server/handlers/mesh.rs
index 9ef6248..af77b56 100644
--- a/makima/src/server/handlers/mesh.rs
+++ b/makima/src/server/handlers/mesh.rs
@@ -601,14 +601,14 @@ pub async fn start_task(
.into_response();
}
- // Get local_only flag from contract if task has one
- let local_only = if let Some(contract_id) = task.contract_id {
+ // Get local_only and auto_merge_local flags from contract if task has one
+ let (local_only, auto_merge_local) = if let Some(contract_id) = task.contract_id {
match repository::get_contract_for_owner(pool, contract_id, auth.owner_id).await {
- Ok(Some(contract)) => contract.local_only,
- _ => false,
+ Ok(Some(contract)) => (contract.local_only, contract.auto_merge_local),
+ _ => (false, false),
}
} else {
- false
+ (false, false)
};
// Get list of daemons that have previously failed this task
@@ -707,6 +707,7 @@ pub async fn start_task(
patch_data: None,
patch_base_sha: None,
local_only,
+ auto_merge_local,
supervisor_worktree_task_id: None, // Not spawned by supervisor
};
@@ -761,6 +762,7 @@ pub async fn start_task(
patch_data: None,
patch_base_sha: None,
local_only,
+ auto_merge_local,
supervisor_worktree_task_id: None, // Not spawned by supervisor
};
@@ -1144,14 +1146,14 @@ pub async fn send_message(
};
if let Ok(Some(updated_task)) = repository::update_task_for_owner(pool, id, auth.owner_id, update_req).await {
- // Get local_only from contract if task has one
- let local_only = if let Some(contract_id) = updated_task.contract_id {
+ // Get local_only and auto_merge_local from contract if task has one
+ let (local_only, auto_merge_local) = if let Some(contract_id) = updated_task.contract_id {
match repository::get_contract_for_owner(pool, contract_id, auth.owner_id).await {
- Ok(Some(contract)) => contract.local_only,
- _ => false,
+ Ok(Some(contract)) => (contract.local_only, contract.auto_merge_local),
+ _ => (false, false),
}
} else {
- false
+ (false, false)
};
// Send spawn command to new daemon
@@ -1177,6 +1179,7 @@ pub async fn send_message(
patch_data: None,
patch_base_sha: None,
local_only,
+ auto_merge_local,
supervisor_worktree_task_id: None, // Not spawned by supervisor
};
@@ -2689,14 +2692,14 @@ pub async fn reassign_task(
}
};
- // Get local_only from contract if task has one
- let local_only = if let Some(contract_id) = task.contract_id {
+ // Get local_only and auto_merge_local from contract if task has one
+ let (local_only, auto_merge_local) = if let Some(contract_id) = task.contract_id {
match repository::get_contract_for_owner(pool, contract_id, auth.owner_id).await {
- Ok(Some(contract)) => contract.local_only,
- _ => false,
+ Ok(Some(contract)) => (contract.local_only, contract.auto_merge_local),
+ _ => (false, false),
}
} else {
- false
+ (false, false)
};
// Send SpawnTask command to daemon for the new task
@@ -2722,6 +2725,7 @@ pub async fn reassign_task(
patch_data,
patch_base_sha,
local_only,
+ auto_merge_local,
supervisor_worktree_task_id: None, // Not spawned by supervisor
};
@@ -3028,14 +3032,14 @@ pub async fn continue_task(
};
let is_orchestrator = task.depth == 0 && subtask_count > 0;
- // Get local_only from contract if task has one
- let local_only = if let Some(contract_id) = task.contract_id {
+ // Get local_only and auto_merge_local from contract if task has one
+ let (local_only, auto_merge_local) = if let Some(contract_id) = task.contract_id {
match repository::get_contract_for_owner(pool, contract_id, auth.owner_id).await {
- Ok(Some(contract)) => contract.local_only,
- _ => false,
+ Ok(Some(contract)) => (contract.local_only, contract.auto_merge_local),
+ _ => (false, false),
}
} else {
- false
+ (false, false)
};
// Send SpawnTask command to daemon
@@ -3061,6 +3065,7 @@ pub async fn continue_task(
patch_data: None,
patch_base_sha: None,
local_only,
+ auto_merge_local,
supervisor_worktree_task_id: None, // Not spawned by supervisor
};
@@ -3989,6 +3994,7 @@ pub async fn branch_task(
patch_data,
patch_base_sha,
local_only: false, // No contract, so not local_only
+ auto_merge_local: false, // No contract, so no auto_merge_local
supervisor_worktree_task_id: None, // Not spawned by supervisor
};
diff --git a/makima/src/server/handlers/mesh_chat.rs b/makima/src/server/handlers/mesh_chat.rs
index 623e66d..eee899f 100644
--- a/makima/src/server/handlers/mesh_chat.rs
+++ b/makima/src/server/handlers/mesh_chat.rs
@@ -1133,14 +1133,14 @@ async fn handle_mesh_request(
}
};
- // Get local_only from contract if task has one
- let local_only = if let Some(contract_id) = task.contract_id {
+ // Get local_only and auto_merge_local from contract if task has one
+ let (local_only, auto_merge_local) = if let Some(contract_id) = task.contract_id {
match repository::get_contract_for_owner(pool, contract_id, owner_id).await {
- Ok(Some(contract)) => contract.local_only,
- _ => false,
+ Ok(Some(contract)) => (contract.local_only, contract.auto_merge_local),
+ _ => (false, false),
}
} else {
- false
+ (false, false)
};
// Send SpawnTask command to daemon
@@ -1166,6 +1166,7 @@ async fn handle_mesh_request(
patch_data: None,
patch_base_sha: None,
local_only,
+ auto_merge_local,
supervisor_worktree_task_id: None, // Not spawned by supervisor
};
diff --git a/makima/src/server/handlers/mesh_supervisor.rs b/makima/src/server/handlers/mesh_supervisor.rs
index 5e74251..3411ec0 100644
--- a/makima/src/server/handlers/mesh_supervisor.rs
+++ b/makima/src/server/handlers/mesh_supervisor.rs
@@ -410,6 +410,7 @@ pub async fn try_start_pending_task(
patch_data,
patch_base_sha,
local_only: contract.local_only,
+ auto_merge_local: contract.auto_merge_local,
// For retried tasks, use their own worktree (they already have state from previous attempt)
supervisor_worktree_task_id: None,
};
@@ -730,6 +731,7 @@ pub async fn spawn_task(
patch_data: None,
patch_base_sha: None,
local_only: contract.local_only,
+ auto_merge_local: contract.auto_merge_local,
// Share supervisor's worktree by default; separate worktree only when explicitly requested
supervisor_worktree_task_id: if request.use_own_worktree { None } else { Some(supervisor_id) },
};
@@ -2257,6 +2259,7 @@ pub async fn resume_supervisor(
patch_data,
patch_base_sha,
local_only: contract.local_only,
+ auto_merge_local: contract.auto_merge_local,
supervisor_worktree_task_id: None, // Supervisor uses its own worktree
};
@@ -2703,6 +2706,7 @@ pub async fn spawn_red_team_task(
patch_data: None,
patch_base_sha: None,
local_only: true, // Red team is always local-only
+ auto_merge_local: false, // Red team doesn't auto-merge
supervisor_worktree_task_id: None,
};
diff --git a/makima/src/server/handlers/transcript_analysis.rs b/makima/src/server/handlers/transcript_analysis.rs
index 920851c..d987d08 100644
--- a/makima/src/server/handlers/transcript_analysis.rs
+++ b/makima/src/server/handlers/transcript_analysis.rs
@@ -279,6 +279,7 @@ pub async fn create_contract_from_analysis(
autonomous_loop: None,
phase_guard: None,
local_only: None,
+ auto_merge_local: None,
red_team_enabled: None,
red_team_prompt: None,
template_id: None,