summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-16 15:09:52 +0000
committersoryu <soryu@soryu.co>2026-02-16 15:09:59 +0000
commit29ec8e53f2acf56fe4a2cd02d352144c697a6afc (patch)
treeae4ad54a3c584fa2e9949f483c9f2b5edd9b819f
parentb6a29bb563499b2fd6280c742bd2106d66393112 (diff)
downloadsoryu-29ec8e53f2acf56fe4a2cd02d352144c697a6afc.tar.gz
soryu-29ec8e53f2acf56fe4a2cd02d352144c697a6afc.zip
Fix bugs with restoring/continuing from tasks
-rw-r--r--makima/src/daemon/task/manager.rs65
-rw-r--r--makima/src/orchestration/directive.rs640
2 files changed, 396 insertions, 309 deletions
diff --git a/makima/src/daemon/task/manager.rs b/makima/src/daemon/task/manager.rs
index 9dc342e..b382507 100644
--- a/makima/src/daemon/task/manager.rs
+++ b/makima/src/daemon/task/manager.rs
@@ -4508,24 +4508,65 @@ impl TaskManagerInner {
);
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(patch_err) => {
+ tracing::warn!(
+ task_id = %task_id,
+ from_task_id = %from_task_id,
+ error = %patch_err,
+ "Patch restore failed — falling back to fresh worktree"
+ );
+ let msg = DaemonMessage::task_output(
+ task_id,
+ format!("Patch restore failed, starting fresh from {}\n", branch),
+ false,
+ );
+ let _ = self.ws_tx.send(msg).await;
+
+ self.worktree_manager
+ .create_worktree(&source_repo, task_id, &task_name, &branch)
+ .await
+ .map_err(|e| DaemonError::Task(TaskError::SetupFailed(e.to_string())))?
}
}
}
- Err(e) => {
- return Err(DaemonError::Task(TaskError::SetupFailed(
- format!("Cannot continue from task {}: worktree copy failed ({}), branch not found ({}), patch decode failed ({})", from_task_id, copy_err, branch_err, e)
- )));
+ Err(decode_err) => {
+ tracing::warn!(
+ task_id = %task_id,
+ from_task_id = %from_task_id,
+ error = %decode_err,
+ "Patch decode failed — falling back to fresh worktree"
+ );
+ let msg = DaemonMessage::task_output(
+ task_id,
+ format!("Patch decode failed, starting fresh from {}\n", branch),
+ false,
+ );
+ let _ = self.ws_tx.send(msg).await;
+
+ self.worktree_manager
+ .create_worktree(&source_repo, task_id, &task_name, &branch)
+ .await
+ .map_err(|e| DaemonError::Task(TaskError::SetupFailed(e.to_string())))?
}
}
} else {
- // Step 4: No fallback available
- return Err(DaemonError::Task(TaskError::SetupFailed(
- format!("Cannot continue from task {}: worktree copy failed ({}), branch not found ({}), no patch data available", from_task_id, copy_err, branch_err)
- )));
+ // Step 4: Fall back to fresh worktree from base branch
+ tracing::warn!(
+ task_id = %task_id,
+ from_task_id = %from_task_id,
+ "All continue_from fallbacks failed — creating fresh worktree from base branch"
+ );
+ let msg = DaemonMessage::task_output(
+ task_id,
+ format!("Source task worktree unavailable, starting fresh from {}\n", branch),
+ false,
+ );
+ let _ = self.ws_tx.send(msg).await;
+
+ self.worktree_manager
+ .create_worktree(&source_repo, task_id, &task_name, &branch)
+ .await
+ .map_err(|e| DaemonError::Task(TaskError::SetupFailed(e.to_string())))?
}
}
}
diff --git a/makima/src/orchestration/directive.rs b/makima/src/orchestration/directive.rs
index 6e0d83d..25aaf1b 100644
--- a/makima/src/orchestration/directive.rs
+++ b/makima/src/orchestration/directive.rs
@@ -9,6 +9,8 @@
use sqlx::PgPool;
use uuid::Uuid;
+use base64::Engine;
+
use crate::db::models::{CreateTaskRequest, UpdateTaskRequest};
use crate::db::repository;
use crate::server::state::{DaemonCommand, SharedState};
@@ -25,11 +27,21 @@ impl DirectiveOrchestrator {
/// Run one orchestration tick — called every 15s.
pub async fn tick(&mut self) -> Result<(), anyhow::Error> {
- self.phase_planning().await?;
- self.phase_execution().await?;
- self.phase_monitoring().await?;
- self.phase_replanning().await?;
- self.phase_completion().await?;
+ if let Err(e) = self.phase_planning().await {
+ tracing::warn!(error = %e, "Directive phase_planning failed, continuing to next phase");
+ }
+ if let Err(e) = self.phase_execution().await {
+ tracing::warn!(error = %e, "Directive phase_execution failed, continuing to next phase");
+ }
+ if let Err(e) = self.phase_monitoring().await {
+ tracing::warn!(error = %e, "Directive phase_monitoring failed, continuing to next phase");
+ }
+ if let Err(e) = self.phase_replanning().await {
+ tracing::warn!(error = %e, "Directive phase_replanning failed, continuing to next phase");
+ }
+ if let Err(e) = self.phase_completion().await {
+ tracing::warn!(error = %e, "Directive phase_completion failed");
+ }
Ok(())
}
@@ -82,8 +94,13 @@ impl DirectiveOrchestrator {
);
// Resolve dependency steps to their task IDs for worktree continuation
- let dep_tasks =
- repository::get_step_dependency_tasks(&self.pool, &step.depends_on).await?;
+ let dep_tasks = match repository::get_step_dependency_tasks(&self.pool, &step.depends_on).await {
+ Ok(deps) => deps,
+ Err(e) => {
+ tracing::warn!(step_id = %step.step_id, error = %e, "Failed to resolve step dependencies — skipping step");
+ continue;
+ }
+ };
let mut continue_from_task_id = dep_tasks.first().map(|d| d.task_id);
// If no dependency tasks resolved, try to continue from previous work:
@@ -214,53 +231,54 @@ impl DirectiveOrchestrator {
let running = repository::get_running_steps_with_tasks(&self.pool).await?;
for step in running {
- match step.task_status.as_str() {
- "completed" | "merged" | "done" => {
- tracing::info!(
- step_id = %step.step_id,
- directive_id = %step.directive_id,
- task_id = %step.task_id,
- "Step task completed — updating step to completed"
- );
- let update = crate::db::models::UpdateDirectiveStepRequest {
- status: Some("completed".to_string()),
- ..Default::default()
- };
- repository::update_directive_step(&self.pool, step.step_id, update).await?;
- repository::advance_directive_ready_steps(&self.pool, step.directive_id)
- .await?;
- repository::check_directive_idle(&self.pool, step.directive_id).await?;
- }
- "failed" | "interrupted" => {
- tracing::warn!(
- step_id = %step.step_id,
- directive_id = %step.directive_id,
- task_id = %step.task_id,
- task_status = %step.task_status,
- "Step task failed — updating step to failed"
- );
- let update = crate::db::models::UpdateDirectiveStepRequest {
- status: Some("failed".to_string()),
- ..Default::default()
- };
- repository::update_directive_step(&self.pool, step.step_id, update).await?;
- repository::advance_directive_ready_steps(&self.pool, step.directive_id)
- .await?;
- repository::check_directive_idle(&self.pool, step.directive_id).await?;
- }
- "paused" => {
- // Task is paused (e.g., waiting for user answer in reconcile mode)
- // Keep step in running status — task will auto-resume when answered
- tracing::debug!(
- step_id = %step.step_id,
- directive_id = %step.directive_id,
- task_id = %step.task_id,
- "Step task paused (waiting for user response) — keeping step running"
- );
- }
- _ => {
- // Still running — do nothing
+ if let Err(e) = async {
+ match step.task_status.as_str() {
+ "completed" | "merged" | "done" => {
+ tracing::info!(
+ step_id = %step.step_id,
+ directive_id = %step.directive_id,
+ task_id = %step.task_id,
+ "Step task completed — updating step to completed"
+ );
+ let update = crate::db::models::UpdateDirectiveStepRequest {
+ status: Some("completed".to_string()),
+ ..Default::default()
+ };
+ repository::update_directive_step(&self.pool, step.step_id, update).await?;
+ repository::advance_directive_ready_steps(&self.pool, step.directive_id)
+ .await?;
+ repository::check_directive_idle(&self.pool, step.directive_id).await?;
+ }
+ "failed" | "interrupted" => {
+ tracing::warn!(
+ step_id = %step.step_id,
+ directive_id = %step.directive_id,
+ task_id = %step.task_id,
+ task_status = %step.task_status,
+ "Step task failed — updating step to failed"
+ );
+ let update = crate::db::models::UpdateDirectiveStepRequest {
+ status: Some("failed".to_string()),
+ ..Default::default()
+ };
+ repository::update_directive_step(&self.pool, step.step_id, update).await?;
+ repository::advance_directive_ready_steps(&self.pool, step.directive_id)
+ .await?;
+ repository::check_directive_idle(&self.pool, step.directive_id).await?;
+ }
+ "paused" => {
+ tracing::debug!(
+ step_id = %step.step_id,
+ directive_id = %step.directive_id,
+ task_id = %step.task_id,
+ "Step task paused (waiting for user response) — keeping step running"
+ );
+ }
+ _ => {}
}
+ Ok::<(), anyhow::Error>(())
+ }.await {
+ tracing::warn!(step_id = %step.step_id, error = %e, "Error processing running step — continuing");
}
}
@@ -268,34 +286,38 @@ impl DirectiveOrchestrator {
let orch_tasks = repository::get_orchestrator_tasks_to_check(&self.pool).await?;
for orch in orch_tasks {
- match orch.task_status.as_str() {
- "completed" | "merged" | "done" => {
- tracing::info!(
- directive_id = %orch.directive_id,
- task_id = %orch.orchestrator_task_id,
- "Planning task completed — clearing orchestrator task"
- );
- repository::clear_orchestrator_task(&self.pool, orch.directive_id).await?;
- // Advance DAG — planning task should have created steps
- repository::advance_directive_ready_steps(&self.pool, orch.directive_id)
+ if let Err(e) = async {
+ match orch.task_status.as_str() {
+ "completed" | "merged" | "done" => {
+ tracing::info!(
+ directive_id = %orch.directive_id,
+ task_id = %orch.orchestrator_task_id,
+ "Planning task completed — clearing orchestrator task"
+ );
+ repository::clear_orchestrator_task(&self.pool, orch.directive_id).await?;
+ repository::advance_directive_ready_steps(&self.pool, orch.directive_id)
+ .await?;
+ }
+ "failed" | "interrupted" => {
+ tracing::warn!(
+ directive_id = %orch.directive_id,
+ task_id = %orch.orchestrator_task_id,
+ "Planning task failed — pausing directive"
+ );
+ repository::clear_orchestrator_task(&self.pool, orch.directive_id).await?;
+ repository::set_directive_status(
+ &self.pool,
+ orch.owner_id,
+ orch.directive_id,
+ "paused",
+ )
.await?;
+ }
+ _ => {}
}
- "failed" | "interrupted" => {
- tracing::warn!(
- directive_id = %orch.directive_id,
- task_id = %orch.orchestrator_task_id,
- "Planning task failed — pausing directive"
- );
- repository::clear_orchestrator_task(&self.pool, orch.directive_id).await?;
- repository::set_directive_status(
- &self.pool,
- orch.owner_id,
- orch.directive_id,
- "paused",
- )
- .await?;
- }
- _ => {}
+ Ok::<(), anyhow::Error>(())
+ }.await {
+ tracing::warn!(directive_id = %orch.directive_id, error = %e, "Error processing orchestrator task — continuing");
}
}
@@ -307,38 +329,43 @@ impl DirectiveOrchestrator {
let directives = repository::get_directives_needing_replanning(&self.pool).await?;
for directive in directives {
- tracing::info!(
- directive_id = %directive.id,
- title = %directive.title,
- "Directive goal updated — spawning re-planning task"
- );
+ if let Err(e) = async {
+ tracing::info!(
+ directive_id = %directive.id,
+ title = %directive.title,
+ "Directive goal updated — spawning re-planning task"
+ );
- let existing_steps =
- repository::list_directive_steps(&self.pool, directive.id).await?;
- let generation =
- repository::get_directive_max_generation(&self.pool, directive.id).await? + 1;
- let goal_history =
- repository::get_directive_goal_history(&self.pool, directive.id, 3).await?;
+ let existing_steps =
+ repository::list_directive_steps(&self.pool, directive.id).await?;
+ let generation =
+ repository::get_directive_max_generation(&self.pool, directive.id).await? + 1;
+ let goal_history =
+ repository::get_directive_goal_history(&self.pool, directive.id, 3).await?;
- let plan =
- build_planning_prompt(&directive, &existing_steps, generation, &goal_history);
+ let plan =
+ build_planning_prompt(&directive, &existing_steps, generation, &goal_history);
- if let Err(e) = self
- .spawn_orchestrator_task(
- directive.id,
- directive.owner_id,
- format!("Re-plan: {}", directive.title),
- plan,
- directive.repository_url.as_deref(),
- directive.base_branch.as_deref(),
- )
- .await
- {
- tracing::warn!(
- directive_id = %directive.id,
- error = %e,
- "Failed to spawn re-planning task"
- );
+ if let Err(e) = self
+ .spawn_orchestrator_task(
+ directive.id,
+ directive.owner_id,
+ format!("Re-plan: {}", directive.title),
+ plan,
+ directive.repository_url.as_deref(),
+ directive.base_branch.as_deref(),
+ )
+ .await
+ {
+ tracing::warn!(
+ directive_id = %directive.id,
+ error = %e,
+ "Failed to spawn re-planning task"
+ );
+ }
+ Ok::<(), anyhow::Error>(())
+ }.await {
+ tracing::warn!(directive_id = %directive.id, error = %e, "Error in re-planning directive — continuing");
}
}
Ok(())
@@ -479,6 +506,19 @@ impl DirectiveOrchestrator {
match repository::update_task_for_owner(&self.pool, task_id, owner_id, update_req).await {
Ok(Some(updated_task)) => {
+ // Load patch data from continue_from task for worktree recovery
+ let (patch_data, patch_base_sha) = if let Some(from_id) = updated_task.continue_from_task_id {
+ match repository::get_latest_checkpoint_patch(&self.pool, from_id).await {
+ Ok(Some(patch)) => {
+ let encoded = base64::engine::general_purpose::STANDARD.encode(&patch.patch_data);
+ (Some(encoded), Some(patch.base_commit_sha))
+ }
+ _ => (None, None),
+ }
+ } else {
+ (None, None)
+ };
+
let command = DaemonCommand::SpawnTask {
task_id,
task_name: task_name.to_string(),
@@ -498,8 +538,8 @@ impl DirectiveOrchestrator {
autonomous_loop: false,
resume_session: false,
conversation_history: None,
- patch_data: None,
- patch_base_sha: None,
+ patch_data,
+ patch_base_sha,
local_only: false,
auto_merge_local: false,
supervisor_worktree_task_id: None,
@@ -539,104 +579,104 @@ impl DirectiveOrchestrator {
let directives = repository::get_idle_directives_needing_completion(&self.pool).await?;
for directive in directives {
- // Atomically claim this directive for completion using a placeholder.
- // This prevents a concurrent tick from also spawning a completion task.
- let placeholder_id = Uuid::new_v4();
- let claimed = repository::claim_directive_for_completion(
- &self.pool,
- directive.id,
- placeholder_id,
- )
- .await?;
-
- if !claimed {
- tracing::debug!(
+ if let Err(e) = async {
+ // Atomically claim this directive for completion using a placeholder.
+ // This prevents a concurrent tick from also spawning a completion task.
+ let placeholder_id = Uuid::new_v4();
+ let claimed = repository::claim_directive_for_completion(
+ &self.pool,
+ directive.id,
+ placeholder_id,
+ )
+ .await?;
+
+ if !claimed {
+ tracing::debug!(
+ directive_id = %directive.id,
+ "Directive already claimed for completion — skipping"
+ );
+ return Ok::<(), anyhow::Error>(());
+ }
+
+ tracing::info!(
directive_id = %directive.id,
- "Directive already claimed for completion — skipping"
+ title = %directive.title,
+ "Directive idle — spawning completion task for PR"
);
- continue;
- }
- tracing::info!(
- directive_id = %directive.id,
- title = %directive.title,
- "Directive idle — spawning completion task for PR"
- );
+ let step_tasks = repository::get_completed_step_tasks(&self.pool, directive.id).await?;
+ if step_tasks.is_empty() {
+ let _ = repository::clear_completion_task(&self.pool, directive.id).await;
+ return Ok(());
+ }
- let step_tasks = repository::get_completed_step_tasks(&self.pool, directive.id).await?;
- if step_tasks.is_empty() {
- // Release the claim since there's nothing to complete
- let _ = repository::clear_completion_task(&self.pool, directive.id).await;
- continue;
- }
+ let base_branch = directive
+ .base_branch
+ .as_deref()
+ .unwrap_or("main");
- let base_branch = directive
- .base_branch
- .as_deref()
- .unwrap_or("main");
+ let directive_branch = format!(
+ "makima/directive-{}-{}",
+ crate::daemon::worktree::sanitize_name(&directive.title),
+ crate::daemon::worktree::short_uuid(directive.id),
+ );
- let directive_branch = format!(
- "makima/directive-{}-{}",
- crate::daemon::worktree::sanitize_name(&directive.title),
- crate::daemon::worktree::short_uuid(directive.id),
- );
+ let step_branches: Vec<String> = step_tasks
+ .iter()
+ .map(|st| {
+ format!(
+ "makima/{}-{}",
+ crate::daemon::worktree::sanitize_name(&st.task_name),
+ crate::daemon::worktree::short_uuid(st.task_id),
+ )
+ })
+ .collect();
- // Compute step branch names using the same formula as execute_completion_action
- let step_branches: Vec<String> = step_tasks
- .iter()
- .map(|st| {
- format!(
- "makima/{}-{}",
- crate::daemon::worktree::sanitize_name(&st.task_name),
- crate::daemon::worktree::short_uuid(st.task_id),
- )
- })
- .collect();
-
- let prompt = build_completion_prompt(
- &directive,
- &step_tasks,
- &step_branches,
- &directive_branch,
- base_branch,
- );
+ let prompt = build_completion_prompt(
+ &directive,
+ &step_tasks,
+ &step_branches,
+ &directive_branch,
+ base_branch,
+ );
- match self
- .spawn_completion_task(
- directive.id,
- directive.owner_id,
- format!("PR: {}", directive.title),
- prompt,
- directive.repository_url.as_deref(),
- directive.base_branch.as_deref(),
- )
- .await
- {
- Ok(task_id) => {
- // Store pr_branch on directive immediately
- let update = crate::db::models::UpdateDirectiveRequest {
- pr_branch: Some(directive_branch.clone()),
- ..Default::default()
- };
- let _ = repository::update_directive_for_owner(
- &self.pool,
- directive.owner_id,
+ match self
+ .spawn_completion_task(
directive.id,
- update,
+ directive.owner_id,
+ format!("PR: {}", directive.title),
+ prompt,
+ directive.repository_url.as_deref(),
+ directive.base_branch.as_deref(),
)
- .await;
- // Replace placeholder with the real task ID
- repository::assign_completion_task(&self.pool, directive.id, task_id).await?;
- }
- Err(e) => {
- tracing::warn!(
- directive_id = %directive.id,
- error = %e,
- "Failed to spawn completion task — releasing claim"
- );
- // Release the claim so it can be retried on the next tick
- let _ = repository::clear_completion_task(&self.pool, directive.id).await;
+ .await
+ {
+ Ok(task_id) => {
+ let update = crate::db::models::UpdateDirectiveRequest {
+ pr_branch: Some(directive_branch.clone()),
+ ..Default::default()
+ };
+ let _ = repository::update_directive_for_owner(
+ &self.pool,
+ directive.owner_id,
+ directive.id,
+ update,
+ )
+ .await;
+ repository::assign_completion_task(&self.pool, directive.id, task_id).await?;
+ }
+ Err(e) => {
+ tracing::warn!(
+ directive_id = %directive.id,
+ error = %e,
+ "Failed to spawn completion task — releasing claim"
+ );
+ let _ = repository::clear_completion_task(&self.pool, directive.id).await;
+ }
}
+ Ok(())
+ }.await {
+ tracing::warn!(directive_id = %directive.id, error = %e, "Error processing directive completion — continuing");
}
}
@@ -644,45 +684,25 @@ impl DirectiveOrchestrator {
let checks = repository::get_completion_tasks_to_check(&self.pool).await?;
for check in checks {
- match check.task_status.as_str() {
- "completed" | "merged" | "done" => {
- tracing::info!(
- directive_id = %check.directive_id,
- task_id = %check.completion_task_id,
- "Completion task finished"
- );
+ if let Err(e) = async {
+ match check.task_status.as_str() {
+ "completed" | "merged" | "done" => {
+ tracing::info!(
+ directive_id = %check.directive_id,
+ task_id = %check.completion_task_id,
+ "Completion task finished"
+ );
- // If directive has no pr_url yet, try to extract from task output
- if check.pr_url.is_none() {
- match self.extract_pr_url_from_task(check.completion_task_id).await {
- Ok(Some(url)) => {
- tracing::info!(
- directive_id = %check.directive_id,
- pr_url = %url,
- "Extracted PR URL from completion task output"
- );
- let update = crate::db::models::UpdateDirectiveRequest {
- pr_url: Some(url),
- ..Default::default()
- };
- let _ = repository::update_directive_for_owner(
- &self.pool,
- check.owner_id,
- check.directive_id,
- update,
- )
- .await;
- }
- Ok(None) => {
- if check.task_name.starts_with("Verify PR:") {
- // Verification task failed to find/create PR — mark directive completed (one-shot)
- tracing::warn!(
+ if check.pr_url.is_none() {
+ match self.extract_pr_url_from_task(check.completion_task_id).await {
+ Ok(Some(url)) => {
+ tracing::info!(
directive_id = %check.directive_id,
- task_id = %check.completion_task_id,
- "Verification task finished but no PR URL found — marking directive completed"
+ pr_url = %url,
+ "Extracted PR URL from completion task output"
);
let update = crate::db::models::UpdateDirectiveRequest {
- status: Some("completed".to_string()),
+ pr_url: Some(url),
..Default::default()
};
let _ = repository::update_directive_for_owner(
@@ -692,37 +712,58 @@ impl DirectiveOrchestrator {
update,
)
.await;
- } else {
+ }
+ Ok(None) => {
+ if check.task_name.starts_with("Verify PR:") {
+ tracing::warn!(
+ directive_id = %check.directive_id,
+ task_id = %check.completion_task_id,
+ "Verification task finished but no PR URL found — marking directive completed"
+ );
+ let update = crate::db::models::UpdateDirectiveRequest {
+ status: Some("archived".to_string()),
+ ..Default::default()
+ };
+ let _ = repository::update_directive_for_owner(
+ &self.pool,
+ check.owner_id,
+ check.directive_id,
+ update,
+ )
+ .await;
+ } else {
+ tracing::warn!(
+ directive_id = %check.directive_id,
+ task_id = %check.completion_task_id,
+ "Completion task finished but no PR URL found — will spawn verifier"
+ );
+ }
+ }
+ Err(e) => {
tracing::warn!(
directive_id = %check.directive_id,
- task_id = %check.completion_task_id,
- "Completion task finished but no PR URL found — will spawn verifier"
+ error = %e,
+ "Failed to extract PR URL from completion task output"
);
}
}
- Err(e) => {
- tracing::warn!(
- directive_id = %check.directive_id,
- error = %e,
- "Failed to extract PR URL from completion task output"
- );
- }
}
- }
- repository::clear_completion_task(&self.pool, check.directive_id).await?;
- }
- "failed" | "interrupted" => {
- tracing::warn!(
- directive_id = %check.directive_id,
- task_id = %check.completion_task_id,
- "Completion task failed"
- );
- repository::clear_completion_task(&self.pool, check.directive_id).await?;
- }
- _ => {
- // Still running
+ repository::clear_completion_task(&self.pool, check.directive_id).await?;
+ }
+ "failed" | "interrupted" => {
+ tracing::warn!(
+ directive_id = %check.directive_id,
+ task_id = %check.completion_task_id,
+ "Completion task failed"
+ );
+ repository::clear_completion_task(&self.pool, check.directive_id).await?;
+ }
+ _ => {}
}
+ Ok::<(), anyhow::Error>(())
+ }.await {
+ tracing::warn!(directive_id = %check.directive_id, error = %e, "Error processing completion task — continuing");
}
}
@@ -731,50 +772,55 @@ impl DirectiveOrchestrator {
repository::get_directives_needing_verification(&self.pool).await?;
for directive in verify_directives {
- let placeholder_id = Uuid::new_v4();
- let claimed = repository::claim_directive_for_completion(
- &self.pool,
- directive.id,
- placeholder_id,
- )
- .await?;
-
- if !claimed {
- continue;
- }
-
- tracing::info!(
- directive_id = %directive.id,
- title = %directive.title,
- "Directive has pr_branch but no pr_url — spawning verification task"
- );
-
- let pr_branch = directive.pr_branch.as_deref().unwrap_or("unknown");
- let base_branch = directive.base_branch.as_deref().unwrap_or("main");
- let prompt = build_verification_prompt(&directive, pr_branch, base_branch);
-
- match self
- .spawn_completion_task(
+ if let Err(e) = async {
+ let placeholder_id = Uuid::new_v4();
+ let claimed = repository::claim_directive_for_completion(
+ &self.pool,
directive.id,
- directive.owner_id,
- format!("Verify PR: {}", directive.title),
- prompt,
- directive.repository_url.as_deref(),
- directive.base_branch.as_deref(),
+ placeholder_id,
)
- .await
- {
- Ok(task_id) => {
- repository::assign_completion_task(&self.pool, directive.id, task_id).await?;
+ .await?;
+
+ if !claimed {
+ return Ok::<(), anyhow::Error>(());
}
- Err(e) => {
- tracing::warn!(
- directive_id = %directive.id,
- error = %e,
- "Failed to spawn verification task — releasing claim"
- );
- let _ = repository::clear_completion_task(&self.pool, directive.id).await;
+
+ tracing::info!(
+ directive_id = %directive.id,
+ title = %directive.title,
+ "Directive has pr_branch but no pr_url — spawning verification task"
+ );
+
+ let pr_branch = directive.pr_branch.as_deref().unwrap_or("unknown");
+ let base_branch = directive.base_branch.as_deref().unwrap_or("main");
+ let prompt = build_verification_prompt(&directive, pr_branch, base_branch);
+
+ match self
+ .spawn_completion_task(
+ directive.id,
+ directive.owner_id,
+ format!("Verify PR: {}", directive.title),
+ prompt,
+ directive.repository_url.as_deref(),
+ directive.base_branch.as_deref(),
+ )
+ .await
+ {
+ Ok(task_id) => {
+ repository::assign_completion_task(&self.pool, directive.id, task_id).await?;
+ }
+ Err(e) => {
+ tracing::warn!(
+ directive_id = %directive.id,
+ error = %e,
+ "Failed to spawn verification task — releasing claim"
+ );
+ let _ = repository::clear_completion_task(&self.pool, directive.id).await;
+ }
}
+ Ok(())
+ }.await {
+ tracing::warn!(directive_id = %directive.id, error = %e, "Error processing verification directive — continuing");
}
}
@@ -1222,10 +1268,10 @@ makima directive update --pr-url "<URL_FROM_GH_PR_CREATE>"
5. If the branch does NOT exist on the remote, the work was likely merged directly.
Mark the directive as completed:
```
-makima directive update --status completed
+makima directive update --status archived
```
-IMPORTANT: You MUST run `makima directive update` with either `--pr-url` or `--status completed` before finishing.
+IMPORTANT: You MUST run `makima directive update` with either `--pr-url` or `--status archived` before finishing.
"#,
title = directive.title,
pr_branch = pr_branch,