def evaluate(c, now="2026-09-16T09:00:00Z"):
    provenance_complete=bool(c.get("provenance_ref"))
    evidence_fresh=(c.get("recommendation_age_days") is not None and c.get("latest_demand_age_days") is not None and c["recommendation_age_days"] <= c["latest_demand_age_days"])
    burst_covered=bool(c.get("burst_observed")) and bool(c.get("seasonality_covered"))
    requests_valid=not (c.get("autoscaling_mode")=="HPA_RESOURCE_UTILIZATION" and not c.get("resource_requests_present"))
    claim=c.get("bottleneck_claim")
    if claim=="MEMORY": bottleneck_supported=(c.get("peak_memory_pct",0)>=90 and c.get("latency_p95_ms",0)>c.get("latency_slo_ms",0))
    elif claim=="CPU": bottleneck_supported=(c.get("peak_cpu_pct",0)>=90 and c.get("latency_p95_ms",0)>c.get("latency_slo_ms",0))
    else: bottleneck_supported=False
    scenarios_comparable=bool(c.get("scenario_recomputed")) and len(c.get("scenarios",[]))>0
    cost_current=bool(c.get("scenario_cost_current"))
    chosen=None
    if provenance_complete and evidence_fresh and burst_covered and requests_valid and bottleneck_supported and scenarios_comparable and cost_current:
        candidates=[s for s in c.get("scenarios",[]) if s.get("addresses_bottleneck") and s.get("constraint_ok") and s.get("rollback_defined") and s.get("expected_capacity_multiplier",0)>=c.get("required_capacity_multiplier",1)]
        if candidates: chosen=min(candidates,key=lambda s:s.get("expected_cost_multiplier",999))
    if not provenance_complete:
        state="UNKNOWN"; decision="BLOCKED_EVIDENCE"; reason="PROVENANCE_MISSING"
    elif not evidence_fresh:
        state="STALE_EVIDENCE"; decision="RECHECK_AFTER_WINDOW"; reason="RECOMMENDATION_PREDATES_NEWEST_DEMAND_EVIDENCE"
    elif not requests_valid:
        state="INSUFFICIENT_DATA"; decision="FIX_RESOURCE_REQUESTS"; reason="HPA_UTILIZATION_UNDEFINED_WITHOUT_RELEVANT_REQUEST"
    elif not burst_covered:
        state="INSUFFICIENT_DATA"; decision="COLLECT_BURST_EVIDENCE"; reason="BURST_OR_SEASONAL_WINDOW_NOT_COVERED"
    elif not bottleneck_supported:
        state="INSUFFICIENT_DATA"; decision="RECHECK_BOTTLENECK"; reason="BOTTLENECK_NOT_SUPPORTED_BY_SATURATION_AND_SERVICE_SIGNAL"
    elif not scenarios_comparable:
        state="NOT_COMPARABLE"; decision="RECOMPUTE_SCENARIOS"; reason="SCENARIOS_NOT_RECOMPUTED_ON_COMMON_EVIDENCE"
    elif not cost_current:
        state="DEFERRED"; decision="REFRESH_SCENARIO_COST"; reason="SCENARIO_COST_EVIDENCE_STALE"
    elif chosen is None:
        state="REJECTED"; decision="REJECT_SCENARIO_SET"; reason="NO_SCENARIO_MEETS_CAPACITY_CONSTRAINT_AND_ROLLBACK"
    elif chosen.get("id")=="NO_CHANGE":
        state="OBSERVE"; decision="NO_CHANGE_OBSERVE"; reason="NO_CHANGE_MEETS_CURRENT_REQUIREMENT"
    else:
        state="RECOMMEND"; decision="PROPOSE_"+chosen.get("id"); reason="BOUNDED_SCENARIO_ADDRESSES_SUPPORTED_BOTTLENECK"
    return {"recommendation_state":state,"next_decision":decision,"evidence_fresh":evidence_fresh,"burst_covered":burst_covered,"bottleneck_supported":bottleneck_supported,"scenarios_comparable":scenarios_comparable,"execution_authorized":bool(c.get("execution_authorization")),"control_reason":reason}
