111 lines
4.6 KiB
Python
111 lines
4.6 KiB
Python
"""Finite state for the single-stage Authoring CDSL protocol."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, replace
|
|
from enum import StrEnum
|
|
|
|
from .errors import ErrorCode
|
|
|
|
|
|
class TaskPhase(StrEnum):
|
|
ANALYZING_REQUEST = "ANALYZING_REQUEST"
|
|
AUTHORING_CDSL = "AUTHORING_CDSL"
|
|
COMPILING_CDSL = "COMPILING_CDSL"
|
|
BUILDING = "BUILDING"
|
|
REPAIRING = "REPAIRING"
|
|
PUBLISHING_BEST_EFFORT = "PUBLISHING_BEST_EFFORT"
|
|
WAITING_FOR_USER = "WAITING_FOR_USER"
|
|
COMPLETED = "COMPLETED"
|
|
FAILED = "FAILED"
|
|
CANCELLED = "CANCELLED"
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class TaskState:
|
|
task_id: str
|
|
phase: TaskPhase
|
|
version: int
|
|
active_revision: str = ""
|
|
repair_count: int = 0
|
|
last_error: ErrorCode | None = None
|
|
retry_from_phase: TaskPhase | None = None
|
|
requirements_path: str = ""
|
|
authoring_path: str = ""
|
|
runtime_cdsl_path: str = ""
|
|
compile_audit_path: str = ""
|
|
diagnostics_path: str = ""
|
|
completion_path: str = ""
|
|
clarification_path: str = ""
|
|
|
|
|
|
_TRANSITIONS: dict[tuple[TaskPhase, str], TaskPhase] = {
|
|
(TaskPhase.ANALYZING_REQUEST, "analysis_written"): TaskPhase.AUTHORING_CDSL,
|
|
(TaskPhase.ANALYZING_REQUEST, "waiting_for_user"): TaskPhase.WAITING_FOR_USER,
|
|
(TaskPhase.WAITING_FOR_USER, "clarification_received"): TaskPhase.ANALYZING_REQUEST,
|
|
(TaskPhase.AUTHORING_CDSL, "authoring_written"): TaskPhase.COMPILING_CDSL,
|
|
(TaskPhase.AUTHORING_CDSL, "repair_required"): TaskPhase.REPAIRING,
|
|
(TaskPhase.COMPILING_CDSL, "compiled"): TaskPhase.BUILDING,
|
|
(TaskPhase.COMPILING_CDSL, "repair_required"): TaskPhase.REPAIRING,
|
|
(TaskPhase.BUILDING, "build_completed"): TaskPhase.PUBLISHING_BEST_EFFORT,
|
|
(TaskPhase.BUILDING, "repair_required"): TaskPhase.REPAIRING,
|
|
(TaskPhase.REPAIRING, "repair_started"): TaskPhase.AUTHORING_CDSL,
|
|
(TaskPhase.PUBLISHING_BEST_EFFORT, "published"): TaskPhase.COMPLETED,
|
|
(TaskPhase.PUBLISHING_BEST_EFFORT, "failed"): TaskPhase.FAILED,
|
|
}
|
|
for _phase in TaskPhase:
|
|
if _phase not in {TaskPhase.COMPLETED, TaskPhase.FAILED, TaskPhase.CANCELLED}:
|
|
_TRANSITIONS[(_phase, "failed")] = TaskPhase.FAILED
|
|
_TRANSITIONS[(_phase, "cancelled")] = TaskPhase.CANCELLED
|
|
if _phase in {
|
|
TaskPhase.AUTHORING_CDSL, TaskPhase.COMPILING_CDSL,
|
|
TaskPhase.BUILDING, TaskPhase.REPAIRING,
|
|
}:
|
|
_TRANSITIONS[(_phase, "publish_best_effort")] = TaskPhase.PUBLISHING_BEST_EFFORT
|
|
|
|
|
|
def legal_transitions() -> dict[tuple[TaskPhase, str], TaskPhase]:
|
|
return dict(_TRANSITIONS)
|
|
|
|
|
|
def transition(
|
|
state: TaskState,
|
|
event: str,
|
|
*,
|
|
active_revision: str | None = None,
|
|
repair_count: int | None = None,
|
|
error: ErrorCode | None = None,
|
|
requirements_path: str | None = None,
|
|
authoring_path: str | None = None,
|
|
runtime_cdsl_path: str | None = None,
|
|
compile_audit_path: str | None = None,
|
|
diagnostics_path: str | None = None,
|
|
completion_path: str | None = None,
|
|
clarification_path: str | None = None,
|
|
) -> TaskState:
|
|
target = state.retry_from_phase if state.phase == TaskPhase.FAILED and event == "resume" else _TRANSITIONS.get((state.phase, event))
|
|
if target is None:
|
|
raise ValueError(f"Illegal single-stage transition: {state.phase.value} --{event}--> ?")
|
|
next_repairs = state.repair_count if repair_count is None else repair_count
|
|
if not 0 <= next_repairs <= 2:
|
|
raise ValueError("Single-stage repair count must be within [0, 2]")
|
|
return replace(
|
|
state,
|
|
phase=target,
|
|
version=state.version + 1,
|
|
active_revision=state.active_revision if active_revision is None else active_revision,
|
|
repair_count=next_repairs,
|
|
last_error=error,
|
|
retry_from_phase=state.phase if target == TaskPhase.FAILED and error in {
|
|
ErrorCode.AUTHOR_TRANSPORT_UNAVAILABLE, ErrorCode.STORAGE_FAILURE,
|
|
ErrorCode.RUNTIME_EXECUTION_FAILURE,
|
|
} else None,
|
|
requirements_path=state.requirements_path if requirements_path is None else requirements_path,
|
|
authoring_path=state.authoring_path if authoring_path is None else authoring_path,
|
|
runtime_cdsl_path=state.runtime_cdsl_path if runtime_cdsl_path is None else runtime_cdsl_path,
|
|
compile_audit_path=state.compile_audit_path if compile_audit_path is None else compile_audit_path,
|
|
diagnostics_path=state.diagnostics_path if diagnostics_path is None else diagnostics_path,
|
|
completion_path=state.completion_path if completion_path is None else completion_path,
|
|
clarification_path=state.clarification_path if clarification_path is None else clarification_path,
|
|
)
|