88 lines
3.1 KiB
Python
88 lines
3.1 KiB
Python
"""Gap detection: active gap classes; any gap triggers halt + root-cause report (no warnings)."""
|
|
|
|
from enum import Enum
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class GapClass(str, Enum):
|
|
"""Active gap classes that trigger immediate halt."""
|
|
|
|
MISSING_NUMERIC_BOUNDS = "missing_numeric_bounds"
|
|
IMPLICIT_TOLERANCES = "implicit_tolerances"
|
|
UNDEFINED_DATUMS = "undefined_datums"
|
|
ASSUMED_PROCESSES = "assumed_processes"
|
|
TOOLPATH_ORPHANING = "toolpath_orphaning"
|
|
|
|
|
|
class GapReport(BaseModel):
|
|
"""Single gap report: class, root-cause, required resolution."""
|
|
|
|
gap_class: GapClass = Field(...)
|
|
description: str = Field(..., description="Human-readable root cause")
|
|
context_ref: str | None = Field(default=None)
|
|
required_resolution: str | None = Field(default=None)
|
|
|
|
|
|
def check_gaps(context: dict[str, Any]) -> list[GapReport]:
|
|
"""Run gap checks on context; any gap triggers halt. Returns list of gap reports; empty = no gaps."""
|
|
reports: list[GapReport] = []
|
|
|
|
if "numeric_bounds" in context:
|
|
nb = context["numeric_bounds"]
|
|
if not isinstance(nb, dict) or not nb:
|
|
reports.append(
|
|
GapReport(
|
|
gap_class=GapClass.MISSING_NUMERIC_BOUNDS,
|
|
description="Numeric bounds missing or empty",
|
|
required_resolution="Provide bounded numeric parameters",
|
|
)
|
|
)
|
|
elif context.get("require_numeric_bounds"):
|
|
reports.append(
|
|
GapReport(
|
|
gap_class=GapClass.MISSING_NUMERIC_BOUNDS,
|
|
description="Numeric bounds required but absent",
|
|
required_resolution="Provide numeric_bounds in context",
|
|
)
|
|
)
|
|
|
|
if context.get("require_explicit_tolerances") and not context.get("tolerances"):
|
|
reports.append(
|
|
GapReport(
|
|
gap_class=GapClass.IMPLICIT_TOLERANCES,
|
|
description="Tolerances must be explicit",
|
|
required_resolution="Declare tolerances in context",
|
|
)
|
|
)
|
|
|
|
if context.get("require_datums") and not context.get("datums"):
|
|
reports.append(
|
|
GapReport(
|
|
gap_class=GapClass.UNDEFINED_DATUMS,
|
|
description="Datums required but undefined",
|
|
required_resolution="Define datums in context",
|
|
)
|
|
)
|
|
|
|
if context.get("require_process_type") and not context.get("process_type"):
|
|
reports.append(
|
|
GapReport(
|
|
gap_class=GapClass.ASSUMED_PROCESSES,
|
|
description="Process type must be declared",
|
|
required_resolution="Set process_type (additive, subtractive, hybrid)",
|
|
)
|
|
)
|
|
|
|
if context.get("toolpath_ref") and not context.get("geometry_lineage") and context.get("require_lineage"):
|
|
reports.append(
|
|
GapReport(
|
|
gap_class=GapClass.TOOLPATH_ORPHANING,
|
|
description="Toolpath must trace to geometry and intent",
|
|
required_resolution="Provide geometry_lineage and intent_ref",
|
|
)
|
|
)
|
|
|
|
return reports
|