"""Versioned, deterministic requirement-conflict resolutions.""" from __future__ import annotations import math from typing import Any REGISTRY_VERSION = "cad.resolution-registry.v1" FULL_CIRCLE_EQUAL_SPACING_VERSION = "full_circle_equal_spacing.v1" class NormalizationError(ValueError): pass def resolve_normalization( *, task_id: str, draft_id: str, source_ids: list[str], description: str, value: dict[str, Any], ) -> dict[str, Any]: if value.get("rule_id") != "full_circle_equal_spacing": raise NormalizationError("Unknown deterministic normalization rule.") count = value.get("count") declared = value.get("declared_spacing_degrees") if not isinstance(count, int) or isinstance(count, bool) or count < 2: raise NormalizationError("Full-circle equal spacing requires an integer count of at least 2.") if value.get("full_circle") is not True: raise NormalizationError("Automatic equal-spacing normalization is allowed only for a full circle.") if not isinstance(declared, (int, float)) or isinstance(declared, bool) or not math.isfinite(float(declared)): raise NormalizationError("Declared angular spacing must be finite.") adopted = 360.0 / count if math.isclose(float(declared), adopted, rel_tol=0.0, abs_tol=1e-9): raise NormalizationError("The declared spacing already matches 360/count; there is no derivable conflict.") return { "schema_version": "cad.requirements-normalization.v1", "registry_version": REGISTRY_VERSION, "rule_id": "full_circle_equal_spacing", "rule_version": FULL_CIRCLE_EQUAL_SPACING_VERSION, "task_id": task_id, "draft_id": draft_id, "source_ids": list(source_ids), "original": {"count": count, "spacing_degrees": float(declared), "full_circle": True}, "adopted": {"count": count, "spacing_degrees": adopted, "full_circle": True}, "reason": description, }