from __future__ import annotations from dataclasses import dataclass from typing import Protocol @dataclass(frozen=True) class NodeResult: status: str edge_result: str = "success" output_metadata: dict[str, object] | None = None telemetry: dict[str, object] | None = None failure_evidence: dict[str, object] | None = None pause_reason: str = "" class NodeHandler(Protocol): node_type: str idempotent: bool replay_safe: bool destructive: bool def run(self, context: object) -> NodeResult: ... class NodeHandlerRegistry: def __init__(self) -> None: self._handlers: dict[str, NodeHandler] = {} def register(self, handler: NodeHandler) -> None: self._handlers[handler.node_type] = handler def get(self, node_type: str) -> NodeHandler: try: return self._handlers[node_type] except KeyError as exc: raise KeyError(f"No graph node handler registered for {node_type}") from exc def has(self, node_type: str) -> bool: return node_type in self._handlers