From b6118a540aa18173ab0db2796915453a4828094b Mon Sep 17 00:00:00 2001 From: soryu Date: Mon, 26 Jan 2026 18:39:19 +0000 Subject: [WIP] Heartbeat checkpoint - 2026-01-26 18:39:19 UTC --- makima/migrations/20250127000000_add_local_only.sql | 8 ++++++++ makima/src/db/repository.rs | 15 ++++++++++----- makima/src/server/handlers/contract_chat.rs | 12 ++++++++++++ makima/src/server/handlers/mesh_chat.rs | 11 +++++++++++ makima/src/server/handlers/transcript_analysis.rs | 1 + 5 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 makima/migrations/20250127000000_add_local_only.sql diff --git a/makima/migrations/20250127000000_add_local_only.sql b/makima/migrations/20250127000000_add_local_only.sql new file mode 100644 index 0000000..2cd594e --- /dev/null +++ b/makima/migrations/20250127000000_add_local_only.sql @@ -0,0 +1,8 @@ +-- Add local_only column to contracts table +-- When enabled, automatic completion actions (branch, merge, pr) are skipped, +-- allowing users to manually handle code changes via patch files or other means. + +ALTER TABLE contracts +ADD COLUMN IF NOT EXISTS local_only BOOLEAN NOT NULL DEFAULT FALSE; + +COMMENT ON COLUMN contracts.local_only IS 'Whether to skip automatic completion actions (branch, merge, pr) for this contract'; diff --git a/makima/src/db/repository.rs b/makima/src/db/repository.rs index d3e4c56..eb95f32 100644 --- a/makima/src/db/repository.rs +++ b/makima/src/db/repository.rs @@ -2175,11 +2175,12 @@ pub async fn create_contract_for_owner( let autonomous_loop = req.autonomous_loop.unwrap_or(false); let phase_guard = req.phase_guard.unwrap_or(false); + let local_only = req.local_only.unwrap_or(false); sqlx::query_as::<_, Contract>( r#" - INSERT INTO contracts (owner_id, name, description, contract_type, phase, autonomous_loop, phase_guard) - VALUES ($1, $2, $3, $4, $5, $6, $7) + INSERT INTO contracts (owner_id, name, description, contract_type, phase, autonomous_loop, phase_guard, local_only) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING * "#, ) @@ -2190,6 +2191,7 @@ pub async fn create_contract_for_owner( .bind(phase) .bind(autonomous_loop) .bind(phase_guard) + .bind(local_only) .fetch_one(pool) .await } @@ -2290,14 +2292,15 @@ pub async fn update_contract_for_owner( let supervisor_task_id = req.supervisor_task_id.or(existing.supervisor_task_id); let autonomous_loop = req.autonomous_loop.unwrap_or(existing.autonomous_loop); let phase_guard = req.phase_guard.unwrap_or(existing.phase_guard); + let local_only = req.local_only.unwrap_or(existing.local_only); let result = if req.version.is_some() { sqlx::query_as::<_, Contract>( r#" UPDATE contracts SET name = $3, description = $4, phase = $5, status = $6, - supervisor_task_id = $7, autonomous_loop = $8, phase_guard = $9, version = version + 1, updated_at = NOW() - WHERE id = $1 AND owner_id = $2 AND version = $10 + supervisor_task_id = $7, autonomous_loop = $8, phase_guard = $9, local_only = $10, version = version + 1, updated_at = NOW() + WHERE id = $1 AND owner_id = $2 AND version = $11 RETURNING * "#, ) @@ -2310,6 +2313,7 @@ pub async fn update_contract_for_owner( .bind(supervisor_task_id) .bind(autonomous_loop) .bind(phase_guard) + .bind(local_only) .bind(req.version.unwrap()) .fetch_optional(pool) .await? @@ -2318,7 +2322,7 @@ pub async fn update_contract_for_owner( r#" UPDATE contracts SET name = $3, description = $4, phase = $5, status = $6, - supervisor_task_id = $7, autonomous_loop = $8, phase_guard = $9, version = version + 1, updated_at = NOW() + supervisor_task_id = $7, autonomous_loop = $8, phase_guard = $9, local_only = $10, version = version + 1, updated_at = NOW() WHERE id = $1 AND owner_id = $2 RETURNING * "#, @@ -2332,6 +2336,7 @@ pub async fn update_contract_for_owner( .bind(supervisor_task_id) .bind(autonomous_loop) .bind(phase_guard) + .bind(local_only) .fetch_optional(pool) .await? }; diff --git a/makima/src/server/handlers/contract_chat.rs b/makima/src/server/handlers/contract_chat.rs index 28c3436..7c3cb7a 100644 --- a/makima/src/server/handlers/contract_chat.rs +++ b/makima/src/server/handlers/contract_chat.rs @@ -1595,6 +1595,16 @@ 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 { + match repository::get_contract_for_owner(pool, contract_id, owner_id).await { + Ok(Some(contract)) => contract.local_only, + _ => false, + } + } else { + false + }; + // Send SpawnTask command to daemon let command = DaemonCommand::SpawnTask { task_id, @@ -1617,6 +1627,7 @@ async fn handle_contract_request( conversation_history: None, patch_data: None, patch_base_sha: None, + local_only, }; if let Err(e) = command_sender.send(command).await { @@ -2625,6 +2636,7 @@ async fn handle_contract_request( initial_phase: Some("research".to_string()), autonomous_loop: None, phase_guard: None, + local_only: None, }; let contract = match repository::create_contract_for_owner(pool, owner_id, contract_req).await { diff --git a/makima/src/server/handlers/mesh_chat.rs b/makima/src/server/handlers/mesh_chat.rs index 1ff0724..eb35728 100644 --- a/makima/src/server/handlers/mesh_chat.rs +++ b/makima/src/server/handlers/mesh_chat.rs @@ -1131,6 +1131,16 @@ 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 { + match repository::get_contract_for_owner(pool, contract_id, owner_id).await { + Ok(Some(contract)) => contract.local_only, + _ => false, + } + } else { + false + }; + // Send SpawnTask command to daemon let command = DaemonCommand::SpawnTask { task_id, @@ -1153,6 +1163,7 @@ async fn handle_mesh_request( conversation_history: None, patch_data: None, patch_base_sha: None, + local_only, }; match state.send_daemon_command(target_daemon_id, command).await { diff --git a/makima/src/server/handlers/transcript_analysis.rs b/makima/src/server/handlers/transcript_analysis.rs index 3b71eca..8eb50c7 100644 --- a/makima/src/server/handlers/transcript_analysis.rs +++ b/makima/src/server/handlers/transcript_analysis.rs @@ -278,6 +278,7 @@ pub async fn create_contract_from_analysis( initial_phase: Some("research".to_string()), autonomous_loop: None, phase_guard: None, + local_only: None, }; let contract = match repository::create_contract_for_owner(pool, auth.owner_id, contract_req).await { -- cgit v1.2.3