where('slug', $planSlug) ->first(); if (! $plan) { throw new \InvalidArgumentException("Plan not found: {$planSlug}"); } $resolved = $this->resolvePhase($plan, $phase); if (! $resolved) { throw new \InvalidArgumentException("Phase not found: {$phase}"); } $tasks = $resolved->tasks ?? []; if (! isset($tasks[$taskIndex])) { throw new \InvalidArgumentException("Task not found at index: {$taskIndex}"); } // Normalise legacy string-format tasks if (is_string($tasks[$taskIndex])) { $tasks[$taskIndex] = ['name' => $tasks[$taskIndex], 'status' => 'pending']; } if ($status !== null) { $tasks[$taskIndex]['status'] = $status; } if ($notes !== null) { $tasks[$taskIndex]['notes'] = $notes; } $resolved->update(['tasks' => $tasks]); return [ 'task' => $tasks[$taskIndex], 'plan_progress' => $plan->fresh()->getProgress(), ]; } private function resolvePhase(AgentPlan $plan, string|int $identifier): ?AgentPhase { if (is_numeric($identifier)) { return $plan->agentPhases()->where('order', (int) $identifier)->first(); } return $plan->agentPhases() ->where(function ($query) use ($identifier) { $query->where('name', $identifier) ->orWhere('order', $identifier); }) ->first(); } }