50 lines
1.9 KiB
Python
50 lines
1.9 KiB
Python
"""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,
|
|
}
|