fix: resume waiting_approval workflows directly instead of re-planning

When a user approves a wait_for_approval step, resume execution from that
step instead of calling process_feedback which may trigger revise_plan
and restart from step 1. Also check state snapshot for WaitingApproval
steps to handle pod restart scenarios where DB status may already be
'executing' but state still has the waiting step.
This commit is contained in:
Fam Zheng
2026-03-10 13:55:47 +00:00
parent 913b448132
commit 5188027d8b

View File

@@ -426,15 +426,21 @@ async fn agent_loop(
.and_then(|json| serde_json::from_str::<AgentState>(&json).ok()) .and_then(|json| serde_json::from_str::<AgentState>(&json).ok())
.unwrap_or_else(AgentState::new); .unwrap_or_else(AgentState::new);
// For failed/done workflows: reset failed steps and continue directly // Resume directly if: workflow is failed/done/waiting_approval,
// For running workflows: process feedback via LLM // OR if state snapshot has a WaitingApproval step (e.g. after pod restart)
let is_resuming = wf.status == "failed" || wf.status == "done"; let has_waiting_step = state.steps.iter().any(|s| matches!(s.status, StepStatus::WaitingApproval));
let is_resuming = wf.status == "failed" || wf.status == "done"
|| wf.status == "waiting_approval" || has_waiting_step;
if is_resuming { if is_resuming {
// Reset Failed steps to Pending so they get re-executed // Reset Failed/WaitingApproval steps so they get re-executed
for step in &mut state.steps { for step in &mut state.steps {
if matches!(step.status, StepStatus::Failed) { if matches!(step.status, StepStatus::Failed) {
step.status = StepStatus::Pending; step.status = StepStatus::Pending;
} }
if matches!(step.status, StepStatus::WaitingApproval) {
// Mark as Running so it continues (not re-plans)
step.status = StepStatus::Running;
}
} }
// Attach comment as feedback to the first actionable step // Attach comment as feedback to the first actionable step
if let Some(order) = state.first_actionable_step() { if let Some(order) = state.first_actionable_step() {
@@ -442,8 +448,8 @@ async fn agent_loop(
step.user_feedbacks.push(content.clone()); step.user_feedbacks.push(content.clone());
} }
} }
tracing::info!("[workflow {}] Resuming from state, first actionable: {:?}", tracing::info!("[workflow {}] Resuming from state (status={}), first actionable: {:?}",
workflow_id, state.first_actionable_step()); workflow_id, wf.status, state.first_actionable_step());
} else { } else {
// Active workflow: LLM decides whether to revise plan // Active workflow: LLM decides whether to revise plan
state = process_feedback( state = process_feedback(