diff --git a/backend/engine/cdsl_engine/capabilities.py b/backend/engine/cdsl_engine/capabilities.py index c73c217a..97df99ba 100644 --- a/backend/engine/cdsl_engine/capabilities.py +++ b/backend/engine/cdsl_engine/capabilities.py @@ -68,12 +68,19 @@ def _has_explicit_host_frame(params: dict[str, Any]) -> bool: def pattern_transform_blocker(source: FeaturePlanNode) -> str | None: """Return the selector dependency that cannot be transformed exactly. - An explicit host frame is coordinate data, not a topology guess. It can - be transformed with a patterned instance while preserving local hole - positions. All topology selectors and selector-dependent extents remain - blocked until their geometry transform contract is implemented. + Selectors fully captured by explicit coordinate data (a revolve axis with + origin/direction, or a host face with a complete world frame) are + transformed together with each patterned instance and are not blockers. + Any remaining topology selector and selector-dependent extent stays + blocked until its geometry transform contract is implemented. """ - if source.selectors: + for selector in source.selectors or (): + if selector.get("kind") == "axis" and _has_explicit_axis(source.params.get("axis")): + # The axis is coordinate data; _translated_node shifts its origin. + continue + if selector.get("kind") == "face" and _has_explicit_host_frame(source.params): + # The host face carries a complete world frame; positions stay local. + continue return "feature selector" host = source.params.get("host_face") if host is not None and not _has_explicit_host_frame(source.params):