CRITICAL=['pilot_id','risky_assumption','hypothesis','minimum_scope','baseline','primary_measure','guardrail_measure','timebox','decision_date','stop_condition','pivot_condition','continue_condition','evidence_ref']
ALLOWED={'CONTINUE','STOP','PIVOT','HOLD','PARTIAL','DISPUTED','STALE','BLOCKED'}

def evaluate(case):
    missing=[k for k in CRITICAL if case.get(k) in (None,'',[])]
    primary_value=case.get('primary_value'); primary_target=case.get('primary_target')
    guardrail_value=case.get('guardrail_value'); guardrail_limit=case.get('guardrail_limit')
    primary_pass=(primary_value is not None and primary_target is not None and float(primary_value)<=float(primary_target))
    guardrail_pass=(guardrail_value is not None and guardrail_limit is not None and float(guardrail_value)<=float(guardrail_limit))
    scope_rebound_ok=not bool(case.get('scope_changed')) or bool(case.get('hypothesis_rebound'))
    threshold_precommitted=not bool(case.get('threshold_changed_after_results'))
    stale=case.get('evidence_age_days',0)>case.get('max_evidence_age_days',30)
    production_forbidden=not bool(case.get('production_ready_claim'))
    state='HOLD'; reason='INSUFFICIENT_EVIDENCE'
    if case.get('blocked_by_owner_policy'):
        state='BLOCKED'; reason='OWNER_OR_POLICY_BLOCK'
    elif missing:
        state='HOLD'; reason='CRITICAL_EVIDENCE_MISSING'
    elif stale:
        state='STALE'; reason='EVIDENCE_STALE'
    elif not threshold_precommitted or not production_forbidden:
        state='DISPUTED'; reason='POST_HOC_RULE_OR_PRODUCTION_CLAIM'
    elif not scope_rebound_ok:
        state='PARTIAL'; reason='SCOPE_CHANGE_NOT_REBOUND'
    elif not guardrail_pass:
        state='STOP'; reason='GUARDRAIL_BREACH'
    elif primary_pass:
        state='CONTINUE'; reason='PRECOMMITTED_CRITERIA_PASS'
    elif case.get('pivot_available'):
        state='PIVOT'; reason='PRIMARY_MISS_PIVOT_TESTABLE'
    else:
        state='STOP'; reason='PRIMARY_MISS_NO_BOUNDED_PIVOT'
    return {'decision_state':state,'reason_code':reason,'primary_pass':primary_pass,'guardrail_pass':guardrail_pass,'scope_rebound_ok':scope_rebound_ok,'threshold_precommitted':threshold_precommitted,'missing_critical_count':len(missing),'production_claim_forbidden':production_forbidden}
