"""Task-Lebenszyklus (→ Konzept 9.2). Übergänge werden serverseitig geprüft.""" from app.core.errors import Invalid QUEUED = "queued" RUNNING = "running" WAITING_APPROVAL = "waiting_approval" WAITING_INPUT = "waiting_input" COMPLETED = "completed" FAILED = "failed" CANCELLED = "cancelled" BLOCKED_BY_GUARDRAIL = "blocked_by_guardrail" NEEDS_RECONCILIATION = "needs_reconciliation" TRANSITIONS: dict[str, set[str]] = { QUEUED: {RUNNING, CANCELLED}, RUNNING: {QUEUED, WAITING_APPROVAL, WAITING_INPUT, COMPLETED, FAILED, CANCELLED, BLOCKED_BY_GUARDRAIL, NEEDS_RECONCILIATION}, WAITING_APPROVAL: {QUEUED, CANCELLED}, WAITING_INPUT: {QUEUED, CANCELLED}, NEEDS_RECONCILIATION: {QUEUED, FAILED}, FAILED: {QUEUED}, COMPLETED: set(), CANCELLED: set(), BLOCKED_BY_GUARDRAIL: set(), } # Zustände, in denen ein Run ohne weiteres Zutun nichts mehr tut (für das Ende eines SSE-Streams) RESTING = {WAITING_APPROVAL, WAITING_INPUT, COMPLETED, FAILED, CANCELLED, BLOCKED_BY_GUARDRAIL, NEEDS_RECONCILIATION} FINAL = {COMPLETED, CANCELLED, BLOCKED_BY_GUARDRAIL} LABELS = {QUEUED: "wartet", RUNNING: "läuft", WAITING_APPROVAL: "wartet auf Freigabe", WAITING_INPUT: "wartet auf Eingabe", COMPLETED: "fertig", FAILED: "fehlgeschlagen", CANCELLED: "abgebrochen", BLOCKED_BY_GUARDRAIL: "blockiert (Guardrail)", NEEDS_RECONCILIATION: "Abgleich nötig"} def check(current: str, target: str) -> None: if target not in TRANSITIONS[current]: raise Invalid(f"Übergang {current} → {target} ist nicht erlaubt.")