summaryrefslogtreecommitdiff
path: root/makima/src/daemon/task/manager.rs
diff options
context:
space:
mode:
Diffstat (limited to 'makima/src/daemon/task/manager.rs')
-rw-r--r--makima/src/daemon/task/manager.rs144
1 files changed, 98 insertions, 46 deletions
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)
- )));
}
}
}