From a6677bafe52d9988c9948df34c1635e4411c9591 Mon Sep 17 00:00:00 2001 From: soryu Date: Fri, 13 Feb 2026 19:19:39 +0000 Subject: Fix worktree branching for directive tasks and remove memories --- makima/src/daemon/task/manager.rs | 144 ++++++++++++++++++++++++++------------ 1 file changed, 98 insertions(+), 46 deletions(-) (limited to 'makima/src/daemon/task') diff --git a/makima/src/daemon/task/manager.rs b/makima/src/daemon/task/manager.rs index 22b41d9..ce5a580 100644 --- a/makima/src/daemon/task/manager.rs +++ b/makima/src/daemon/task/manager.rs @@ -20,7 +20,7 @@ use crate::daemon::error::{DaemonError, TaskError, TaskResult}; use crate::daemon::process::{ClaudeInputMessage, ProcessManager}; use crate::daemon::storage; use crate::daemon::temp::TempManager; -use crate::daemon::worktree::{is_new_repo_request, ConflictResolution, WorktreeInfo, WorktreeManager}; +use crate::daemon::worktree::{is_new_repo_request, ConflictResolution, WorktreeError, WorktreeInfo, WorktreeManager}; use crate::daemon::db::local::LocalDb; use crate::daemon::ws::{BranchInfo, DaemonCommand, DaemonMessage}; @@ -4406,8 +4406,14 @@ impl TaskManagerInner { // Create worktree - either from scratch or copying from another task let task_name = format!("task-{}", &task_id.to_string()[..8]); let worktree_info = if let Some(from_task_id) = continue_from_task_id { - // Try to find the source task's worktree path - match self.find_worktree_for_task(from_task_id).await { + // Fallback chain for continuing from a previous task: + // 1. Try copying from existing worktree (fastest, preserves uncommitted changes) + // 2. Try creating from pushed branch (branch was pushed to remote) + // 3. Try restoring from saved patch data + // 4. Fail if none available + + // Step 1: Try copying from existing worktree + let copy_result = match self.find_worktree_for_task(from_task_id).await { Ok(source_worktree) => { let msg = DaemonMessage::task_output( task_id, @@ -4416,66 +4422,112 @@ impl TaskManagerInner { ); let _ = self.ws_tx.send(msg).await; - // Create worktree by copying from source task self.worktree_manager .create_worktree_from_task(&source_worktree, task_id, &task_name) .await - .map_err(|e| DaemonError::Task(TaskError::SetupFailed(e.to_string())))? } - Err(worktree_err) => { - // Source worktree not found - try to recover using patch data - if let (Some(patch_str), Some(base_sha)) = (&patch_data, &patch_base_sha) { - tracing::info!( - task_id = %task_id, - from_task_id = %from_task_id, - base_sha = %base_sha, - patch_len = patch_str.len(), - "Source worktree not found, attempting to restore from patch" - ); + Err(e) => Err(crate::daemon::worktree::WorktreeError::RepoNotFound(e.to_string())), + }; - let msg = DaemonMessage::task_output( - task_id, - format!("Source worktree unavailable, restoring from checkpoint patch...\n"), - false, - ); - let _ = self.ws_tx.send(msg).await; + match copy_result { + Ok(info) => info, + Err(copy_err) => { + tracing::warn!( + task_id = %task_id, + from_task_id = %from_task_id, + error = %copy_err, + "Failed to copy from source worktree, trying branch fallback" + ); + + // Step 2: Try creating from pushed branch + let msg = DaemonMessage::task_output( + task_id, + format!("Source worktree unavailable, checking for pushed branch...\n"), + false, + ); + let _ = self.ws_tx.send(msg).await; + + match self.worktree_manager + .create_worktree_from_task_branch(&source_repo, from_task_id, task_id, &task_name) + .await + { + Ok(info) => { + tracing::info!( + task_id = %task_id, + from_task_id = %from_task_id, + branch = %info.branch, + "Successfully created worktree from pushed branch" + ); + let msg = DaemonMessage::task_output( + task_id, + format!("Restored from pushed branch {}\n", info.branch), + false, + ); + let _ = self.ws_tx.send(msg).await; + info + } + Err(branch_err) => { + tracing::warn!( + task_id = %task_id, + from_task_id = %from_task_id, + error = %branch_err, + "No pushed branch found, trying patch fallback" + ); + + // Step 3: Try restoring from saved patch data + if let (Some(patch_str), Some(base_sha)) = (&patch_data, &patch_base_sha) { + tracing::info!( + task_id = %task_id, + from_task_id = %from_task_id, + base_sha = %base_sha, + patch_len = patch_str.len(), + "Attempting to restore from checkpoint patch" + ); - // Decode base64 patch data - match base64::Engine::decode(&base64::engine::general_purpose::STANDARD, patch_str) { - Ok(patch_bytes) => { - match self.worktree_manager.restore_from_patch( - source, + let msg = DaemonMessage::task_output( task_id, - &task_name, - base_sha, - &patch_bytes, - ).await { - Ok(worktree_info) => { - tracing::info!( - task_id = %task_id, - path = %worktree_info.path.display(), - "Successfully restored worktree from patch" - ); - worktree_info + format!("Restoring from checkpoint patch...\n"), + false, + ); + let _ = self.ws_tx.send(msg).await; + + match base64::Engine::decode(&base64::engine::general_purpose::STANDARD, patch_str) { + Ok(patch_bytes) => { + match self.worktree_manager.restore_from_patch( + source, + task_id, + &task_name, + base_sha, + &patch_bytes, + ).await { + Ok(worktree_info) => { + tracing::info!( + task_id = %task_id, + path = %worktree_info.path.display(), + "Successfully restored worktree from patch" + ); + worktree_info + } + Err(e) => { + return Err(DaemonError::Task(TaskError::SetupFailed( + format!("Cannot continue from task {}: worktree copy failed ({}), branch not found ({}), patch restore failed ({})", from_task_id, copy_err, branch_err, e) + ))); + } + } } Err(e) => { return Err(DaemonError::Task(TaskError::SetupFailed( - format!("Cannot continue from task {}: {} (patch restore also failed: {})", from_task_id, worktree_err, e) + format!("Cannot continue from task {}: worktree copy failed ({}), branch not found ({}), patch decode failed ({})", from_task_id, copy_err, branch_err, e) ))); } } - } - Err(e) => { + } else { + // Step 4: No fallback available return Err(DaemonError::Task(TaskError::SetupFailed( - format!("Cannot continue from task {}: {} (patch decode failed: {})", from_task_id, worktree_err, e) + format!("Cannot continue from task {}: worktree copy failed ({}), branch not found ({}), no patch data available", from_task_id, copy_err, branch_err) ))); } } - } else { - // No patch data available - fail with original error - return Err(DaemonError::Task(TaskError::SetupFailed( - format!("Cannot continue from task {}: {}", from_task_id, worktree_err) - ))); } } } -- cgit v1.2.3