from __future__ import annotations from typing import Any CANONICAL_TASK_STATUSES: tuple[str, ...] = ( "wait", "todo", "progress", "done", "cancel", ) TASK_STATUS_CODES: dict[str, str] = { "WAIT": "wait", "TODO": "todo", "PROG": "progress", "DONE": "done", "CNCL": "cancel", } LEGACY_TASK_STATUS_ALIASES: dict[str, str] = { "blocked": "wait", "waiting": "wait", "in_progress": "progress", "in-progress": "progress", "prog": "progress", "cancelled": "cancel", "canceled": "cancel", "cncl": "cancel", } OPEN_TASK_STATUSES: frozenset[str] = frozenset({"wait", "todo", "progress"}) ACTIVE_TASK_STATUSES: frozenset[str] = frozenset({"wait", "progress"}) TERMINAL_TASK_STATUSES: frozenset[str] = frozenset({"done", "cancel"}) TASK_STATUS_ORDER: dict[str, int] = { "todo": 0, "wait": 1, "progress": 1, "done": 2, "cancel": 2, } TASK_STATUS_LABELS: dict[str, str] = { "wait": "wait", "todo": "todo", "progress": "progress", "done": "done", "cancel": "cancel", } def raw_status_value(status: Any) -> str: if hasattr(status, "value"): status = status.value return str(status or "").strip() def normalize_task_status(status: Any, *, default: str | None = None) -> str: """Normalize canon task statuses plus legacy aliases to stored values.""" value = raw_status_value(status).lower() if not value: if default is not None: return default raise ValueError("task status is required") value = LEGACY_TASK_STATUS_ALIASES.get(value, value) if value not in CANONICAL_TASK_STATUSES: allowed = ", ".join(CANONICAL_TASK_STATUSES) aliases = ", ".join(sorted(LEGACY_TASK_STATUS_ALIASES)) raise ValueError(f"task status must be one of {allowed}; legacy aliases: {aliases}") return value def status_value(status: Any, *, default: str | None = None) -> str: """Compatibility wrapper used by lifecycle and reconciliation code.""" return normalize_task_status(status, default=default) def is_open_task_status(status: Any) -> bool: return normalize_task_status(status, default="todo") in OPEN_TASK_STATUSES def is_terminal_task_status(status: Any) -> bool: return normalize_task_status(status, default="todo") in TERMINAL_TASK_STATUSES