def evaluate(c,evaluation_time):
    fresh=c.get("source_freshness")=="CURRENT"
    prov=bool(c.get("provenance_complete"))
    obs_sep=bool(c.get("observation_frozen") and c.get("observation_window_fixed") and c.get("observation_query_fixed") and c.get("hypothesis_falsifiable"))
    change_isolated=bool(c.get("treatment_predeclared") and c.get("treatment_change_count")==1)
    assignment_valid=bool(c.get("control_present") and c.get("experimental_unit_defined") and c.get("assignment_rule") in ("RANDOMIZED","DETERMINISTIC_HASH","CLUSTER_RANDOMIZED") and c.get("interference_control"))
    rules_predeclared=bool(c.get("primary_metric_predeclared") and c.get("guardrails_predeclared") and c.get("stopping_rule_predeclared") and c.get("analysis_plan_predeclared") and c.get("decision_rule_predeclared") and not c.get("primary_metric_changed_after_result") and not c.get("hypothesis_changed_after_result"))
    causal=False
    if not fresh:
        state,decision,reason="STALE_EVIDENCE","REFRESH_EVIDENCE","SOURCE_EVIDENCE_STALE"
    elif not prov or c.get("assignment_rule") is None:
        state,decision,reason="UNKNOWN","DEFER_MISSING_EVIDENCE","PROVENANCE_OR_ASSIGNMENT_UNKNOWN"
    elif not obs_sep:
        state,decision,reason="DESIGN_REPAIR_REQUIRED","FREEZE_OBSERVATION","OBSERVATION_NOT_FROZEN"
    elif not change_isolated:
        state,decision,reason="DESIGN_REPAIR_REQUIRED","ISOLATE_TREATMENT","MULTIPLE_TREATMENT_CHANGES"
    elif not assignment_valid:
        state,decision,reason="DESIGN_REPAIR_REQUIRED","REPAIR_ASSIGNMENT","ASSIGNMENT_OR_INTERFERENCE_INVALID"
    elif c.get("result_available") and (c.get("hypothesis_changed_after_result") or c.get("primary_metric_changed_after_result")):
        state,decision,reason="CAUSAL_CLAIM_BLOCKED","DO_NOT_CLAIM_CAUSALITY","POSTHOC_HYPOTHESIS_OR_PRIMARY_METRIC"
    elif not rules_predeclared:
        state,decision,reason="DESIGN_REPAIR_REQUIRED","LOCK_PREDECLARATIONS","PREDECLARATION_INCOMPLETE"
    elif c.get("result_available"):
        if not c.get("instrumentation_valid") or not c.get("uncertainty_reported"):
            state,decision,reason="CAUSAL_CLAIM_BLOCKED","DO_NOT_CLAIM_CAUSALITY","RESULT_EVIDENCE_INCOMPLETE"
        else:
            state,decision,reason="RESULT_INTERPRETABLE","REPORT_BOUNDED_EFFECT","VALID_DESIGN_AND_RESULT_EVIDENCE"; causal=True
    else:
        state,decision,reason="DESIGN_READY","RUN_BOUNDED_EXPERIMENT","PRE_EXPOSURE_CONTRACT_READY"
    return {"experiment_state":state,"decision":decision,"observation_hypothesis_separated":obs_sep,"change_isolated":change_isolated,"assignment_valid":assignment_valid,"rules_predeclared":rules_predeclared,"causal_claim_allowed":causal,"control_reason":reason}
