from __future__ import annotations from biliup_next.app.bootstrap import ensure_initialized from biliup_next.app.task_audit import record_task_action from biliup_next.app.task_engine import ( execute_step, next_runnable_step, ) from biliup_next.app.task_policies import apply_disabled_step_fallbacks from biliup_next.app.task_policies import resolve_failure from biliup_next.core.errors import ModuleError from biliup_next.core.models import utc_now_iso def process_task(task_id: str, *, reset_step: str | None = None, include_stage_scan: bool = False) -> dict[str, object]: state = ensure_initialized() repo = state["repo"] task = repo.get_task(task_id) if task is None: return {"processed": [], "error": {"code": "TASK_NOT_FOUND", "message": f"task not found: {task_id}"}} processed: list[dict[str, object]] = [] if include_stage_scan: ingest_settings = dict(state["settings"]["ingest"]) ingest_settings.update(state["settings"]["paths"]) stage_scan = state["ingest_service"].scan_stage(ingest_settings) processed.append({"stage_scan": stage_scan}) record_task_action(repo, task_id, "stage_scan", "ok", "stage scan completed", stage_scan) if reset_step: step_names = {step.step_name for step in repo.list_steps(task_id)} if reset_step not in step_names: return {"processed": processed, "error": {"code": "STEP_NOT_FOUND", "message": f"step not found: {reset_step}"}} repo.update_step_status( task_id, reset_step, "pending", error_code=None, error_message=None, started_at=None, finished_at=None, ) repo.update_task_status(task_id, task.status, utc_now_iso()) processed.append({"task_id": task_id, "step": reset_step, "reset": True}) record_task_action(repo, task_id, "retry_step", "ok", f"step reset to pending: {reset_step}", {"step_name": reset_step}) try: while True: current_task = repo.get_task(task.id) or task current_steps = {step.step_name: step for step in repo.list_steps(task.id)} if apply_disabled_step_fallbacks(state, current_task, repo): continue step_name, waiting_payload = next_runnable_step(current_task, current_steps, state) if waiting_payload is not None: processed.append(waiting_payload) return {"processed": processed} if step_name is None: break payload = execute_step(state, task.id, step_name) if current_task.status == "failed_retryable": payload["retry"] = True record_task_action(repo, task_id, step_name, "ok", f"{step_name} retry succeeded", payload) else: record_task_action(repo, task_id, step_name, "ok", f"{step_name} succeeded", payload) processed.append(payload) except ModuleError as exc: failure = resolve_failure(task, repo, state, exc) processed.append(failure["payload"]) record_task_action(repo, task_id, failure["step_name"], "error", failure["summary"], failure["payload"]) return {"processed": processed}