70 lines
2.9 KiB
Python
70 lines
2.9 KiB
Python
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
|