CRITICAL = ["asset_id","asset_version","source_asset_ref","ingredient_refs","activity_id","agent_id","content_hash","credential_ref","credential_validation_state","rights_state","approval_state","evidence_ref"]

def evaluate(c):
    missing=[k for k in CRITICAL if c.get(k) in (None,"",[],{})]
    evidence_complete=not missing
    is_derived=c.get("derivation_type") in {"DERIVED","COMPOSED"}
    source_graph_valid=(not is_derived) or bool(c.get("source_asset_ref"))
    declared=int(c.get("declared_ingredient_count",0) or 0)
    ingredients=c.get("ingredient_refs") or []
    ingredient_graph_valid=(declared==len(ingredients)) and (c.get("derivation_type")!="COMPOSED" or declared>0)
    hash_valid=c.get("hash_matches") is True
    credential_valid=c.get("credential_validation_state")=="VALID"
    rights_valid=c.get("rights_state")=="APPROVED"
    approval_valid=c.get("approval_state")=="APPROVED_CURRENT_VERSION"
    overclaim=credential_valid and c.get("rights_inferred_from_credential") is True
    governance_valid=rights_valid and approval_valid and not overclaim

    if not evidence_complete:
        state="HOLD_MISSING_EVIDENCE"
    elif not source_graph_valid:
        state="BLOCKED_SOURCE_LINK"
    elif not ingredient_graph_valid:
        state="BLOCKED_INGREDIENT_GAP"
    elif not hash_valid:
        state="BLOCKED_HASH_MISMATCH"
    elif not credential_valid:
        state="BLOCKED_CREDENTIAL_INVALID"
    elif overclaim:
        state="BLOCKED_CREDENTIAL_OVERCLAIM"
    elif not rights_valid:
        state="BLOCKED_RIGHTS"
    elif not approval_valid:
        state="BLOCKED_APPROVAL"
    else:
        state="DOWNSTREAM_USE_ALLOWED"
    return {"state":state,"evidence_complete":evidence_complete,"source_graph_valid":source_graph_valid and ingredient_graph_valid,"hash_valid":hash_valid,"credential_valid":credential_valid,"governance_valid":governance_valid}
