from __future__ import annotations from copy import deepcopy from pathlib import Path import re from typing import Any def _last_executable_prefix( cdsl: dict[str, Any], failed_feature_id: str | None, output: Path, ) -> dict[str, Any] | None: """Bind and export the longest verified prefix before a failed feature.""" from .selector_binding import bind_candidate_selectors from engine.cdsl_engine.runtime import rebuild_cdsl features = list(cdsl.get("features") or []) failed_index = next( (index for index, feature in enumerate(features) if feature.get("id") == failed_feature_id), len(features), ) for feature_count in range(failed_index, 0, -1): prefix = deepcopy(cdsl) prefix["features"] = features[:feature_count] try: bound_prefix, _binding = bind_candidate_selectors(prefix) result = rebuild_cdsl(bound_prefix, output, strict=True) except Exception: continue return { "failed_feature_id": failed_feature_id, "feature_count": feature_count, "last_feature_id": str(features[feature_count - 1].get("id") or ""), "bound_cdsl": bound_prefix, "result": result, } return None def _failed_feature_id(error: Exception) -> str | None: diagnostic = getattr(error, "diagnostic", None) feature_id = getattr(diagnostic, "feature_id", None) if isinstance(feature_id, str) and feature_id: return feature_id match = re.match(r"([^:\s]+): ", str(error)) return match.group(1) if match else None def rebuild_candidate(cdsl: dict[str, Any], output: Path) -> dict[str, Any]: from engine.cdsl_engine.runtime import analyze_cdsl, rebuild_cdsl from .selector_binding import bind_candidate_selectors analysis = analyze_cdsl(cdsl) analysis_dict = analysis.as_dict() if hasattr(analysis, "as_dict") else {"runtime_eligible": analysis.runtime_eligible} if not analysis.runtime_eligible: return {"status": "runtime_ineligible", "analysis": analysis_dict} bound: dict[str, Any] | None = None try: bound, binding = bind_candidate_selectors(cdsl) result = rebuild_cdsl(bound, output, strict=True) return {"status": "rebuilt", "analysis": analysis_dict, "selector_binding": binding, "bound_cdsl": bound, "result": result} except Exception as exc: detail = {"type": type(exc).__name__, "message": str(exc)} if hasattr(exc, "selector_resolutions"): detail["selector_resolutions"] = exc.selector_resolutions prefix = _last_executable_prefix(cdsl, _failed_feature_id(exc), output) result = {"status": "rebuild_failed", "analysis": analysis_dict, "error": detail} if bound is not None: result["bound_cdsl"] = bound if prefix is not None: result["last_executable_prefix"] = prefix return result