from __future__ import annotations
MISSING={None,"","MISSING","UNKNOWN","N/A"}
REQ=['project_id', 'driving_problem', 'target_competency_id', 'milestone_id', 'evidence_opportunity', 'pre_step_prompt', 'learner_response_ref', 'misconception_signal', 'jit_instruction_id', 'support_reason', 'checkpoint_state', 'revision_cycle', 'public_or_reviewable_output', 'transfer_variant', 'exit_evidence']

def missing(v): return v in MISSING

def evaluate(x):
    # identity / boundary fields
    for f in ["project_id","driving_problem","target_competency_id","milestone_id"]:
        if missing(x.get(f)): return "HOLD_CORE_PROJECT_FIELD_MISSING"
    if missing(x.get("evidence_opportunity")):
        if x.get("checkpoint_state") in {"PROJECT_COMPLETE","MASTERED","EVIDENCE_CLOSED"}: return "HOLD_COMPLETION_AS_MASTERY"
        return "HOLD_PROJECT_EVIDENCE_MISSING"
    if missing(x.get("pre_step_prompt")): return "HOLD_PRESTEP_PROMPT_MISSING"
    if missing(x.get("learner_response_ref")): return "HOLD_RESPONSE_REF_MISSING"
    if missing(x.get("misconception_signal")): return "HOLD_SIGNAL_NOT_TRACEABLE"
    # project artifact has authority over verbal confidence
    if x.get("artifact_quality") == "WRONG" and x.get("pre_step_correct") is True:
        return "HOLD_ARTIFACT_CONTRADICTS_PRESTEP"
    # explicit JIT decisions
    jit=x.get("jit_instruction_id")
    if not missing(jit) and jit!="NONE":
        if missing(x.get("support_reason")): return "HOLD_SUPPORT_REASON_MISSING"
        if x.get("independent_evidence_closed") is True: return "HOLD_UNNECESSARY_JIT_SUPPORT"
    if x.get("checkpoint_state") in {"MASTERED_FROM_SUPPORT","MASTERED_FROM_JIT","SUPPORT_COMPLETE_AS_MASTERY"}:
        return "HOLD_SUPPORT_AS_MASTERY"
    if missing(x.get("checkpoint_state")): return "HOLD_CHECKPOINT_MISSING"
    if x.get("checkpoint_state") in {"EVIDENCE_CLOSED","TRANSFER_EVIDENCE_REVIEW","PROJECT_COMPLETE","MASTERED"} and x.get("artifact_quality") in {"WRONG","MISSING"}:
        return "HOLD_CHECKPOINT_UNSUPPORTED"
    if missing(x.get("revision_cycle")): return "HOLD_REVISION_HISTORY_MISSING"
    if missing(x.get("public_or_reviewable_output")): return "HOLD_REVIEWABLE_OUTPUT_MISSING"
    if missing(x.get("transfer_variant")): return "HOLD_TRANSFER_VARIANT_MISSING"
    if missing(x.get("exit_evidence")) or x.get("exit_evidence")=="HOLD": return "HOLD_EXIT_EVIDENCE_MISSING"
    if x.get("transfer_variant") not in {"BASELINE","NONE"}:
        if x.get("baseline_decision_reused") is True: return "HOLD_TRANSFER_BASELINE_ORACLE_REUSE"
        if x.get("transfer_recomputed") is not True: return "HOLD_TRANSFER_NOT_RECOMPUTED"
        return "TRANSFER_REVIEW_READY"
    if x.get("independent_evidence_closed") is True and (missing(jit) or jit=="NONE"):
        return "NO_JIT_REQUIRED_EVIDENCE_ALREADY_INDEPENDENT"
    return "LOOP_REVIEW_READY"
