"""Portable CPU-only EARS Steering v2, using the published recipe contract.

raw_scores is the frozen raw_ears dictionary in a packed take's JSON.
specification is recipe['steering_contract'] from agents/recipes.json.
No model, local path, network request or credential is required.
"""
import math

def steering_score(raw_scores,specification):
    if not raw_scores:return 0.0
    total=0.0
    if abs(sum(t['weight'] for t in specification['targets'])-1)>1e-9:
        raise ValueError('Target weights must sum to one')
    for target in specification['targets']:
        raw=float(raw_scores[target['head']])
        if not math.isfinite(raw):raise ValueError('Nonfinite EARS score')
        z=(raw-target['mean'])/max(target['std'],1e-8)
        normalized=.5*(1+math.erf(z/math.sqrt(2)))
        if target['direction']=='low':normalized=1-normalized
        total+=target['weight']*normalized
    return total
