"""Pinned compliance spec for the gitea-workflow skill + drift detection.

The auto-generated spec from skill-comply drifted between runs (the dry run
marked four steps required; the full run marked only initialization). The
pinned spec here is the source of truth: the critical merge workflow steps
are always required, and check_spec_drift() flags any generated spec that
omits or downgrades them.
"""

import json
from pathlib import Path

CRITICAL_MERGE_STEPS = (
    "initialize_tools_and_context",
    "inspect_pr_state",
    "perform_independent_review",
    "obtain_explicit_merge_approval",
    "refuse_competing_skip_review",
    "avoid_blind_merge",
    "execute_merge_after_gates",
    "cleanup_only_when_permitted",
)

DEFAULT_SPEC_PATH = Path(__file__).parent / "specs" / "gitea-workflow.json"


class SpecValidationError(Exception):
    """Raised when a spec omits or downgrades a critical merge step."""


def load_pinned_spec(path=None):
    """Load and validate the pinned spec. Fails closed on any drift."""
    spec_path = Path(path) if path else DEFAULT_SPEC_PATH
    spec = json.loads(spec_path.read_text())
    steps = {s["id"]: s for s in spec.get("steps", [])}
    for step_id in CRITICAL_MERGE_STEPS:
        if step_id not in steps:
            raise SpecValidationError(
                f"pinned spec is missing critical step '{step_id}'")
        if not steps[step_id].get("required"):
            raise SpecValidationError(
                f"critical step '{step_id}' must be required, not optional")
    return spec


def check_spec_drift(generated_steps, critical_steps=CRITICAL_MERGE_STEPS):
    """Compare a generated spec's steps against the critical step list.

    *generated_steps* is a list of {"id": str, "required": bool} dicts (the
    shape skill-comply emits). Returns a list of human-readable drift
    findings; empty means no drift.
    """
    by_id = {s["id"]: s for s in generated_steps}
    drift = []
    for step_id in critical_steps:
        if step_id not in by_id:
            drift.append(
                f"critical step '{step_id}' is missing from the generated spec")
        elif not by_id[step_id].get("required"):
            drift.append(
                f"critical step '{step_id}' was generated as optional but "
                "must be required")
    return drift
