"""Franka-only direct-weight revision; no training or simulation.

The inherited analytic compiler is reproduced here, not imported from another
policy. Only its lateral reference is changed. The ten supplied development
traces motivated the change; no parameters are fitted to their actions.
Run with NumPy. Reads public_models and writes exactly the requested artifacts.
"""
from pathlib import Path
import json
import xml.etree.ElementTree as ET
import numpy as np
BASE=Path(__file__).resolve().parent
ROOT=BASE/'public_models'
PUBLIC=json.loads((ROOT/'franka_public.json').read_text())
HOME=np.array(PUBLIC['home'],dtype=float)

def numbers(s,default):
    return np.fromstring(s,sep=' ') if s is not None else np.array(default,dtype=float)
def quat_rotation(q):
    w,x,y,z=q/np.linalg.norm(q)
    return np.array([[1-2*(y*y+z*z),2*(x*y-z*w),2*(x*z+y*w)],
                     [2*(x*y+z*w),1-2*(x*x+z*z),2*(y*z-x*w)],
                     [2*(x*z-y*w),2*(y*z+x*w),1-2*(x*x+y*y)]])
def rz(q):
    c,s=np.cos(q),np.sin(q)
    return np.array([[c,-s,0],[s,c,0],[0,0,1.]])
def fixed_transform(body):
    t=np.eye(4)
    t[:3,:3]=quat_rotation(numbers(body.get('quat'),[1,0,0,0]))
    t[:3,3]=numbers(body.get('pos'),[0,0,0])
    return t
def inertia_matrix(e):
    if e.get('fullinertia') is not None:
        xx,yy,zz,xy,xz,yz=numbers(e.get('fullinertia'),[])
        return np.array([[xx,xy,xz],[xy,yy,yz],[xz,yz,zz]])
    R=quat_rotation(numbers(e.get('quat'),[1,0,0,0]))
    return R@np.diag(numbers(e.get('diaginertia'),[]))@R.T

def static_home_model(xmlroot):
    """Single static pose, inertial sums only. No dynamics or integration."""
    M=np.eye(7)*.1; g=np.zeros(7); found={}
    def visit(body,parent,axes,origins):
        nonlocal M,g
        T=parent@fixed_transform(body)
        axes,origins=dict(axes),dict(origins)
        joint=body.find('joint')
        if joint is not None:
            j=int(joint.get('name').replace('joint',''))-1
            axes[j]=T[:3,:3]@numbers(joint.get('axis'),[0,0,1])
            origins[j]=T[:3,3].copy()
            R=np.eye(4); R[:3,:3]=rz(HOME[j]); T=T@R
        def jac(p):
            V,A=np.zeros((3,7)),np.zeros((3,7))
            for j in axes:
                A[:,j]=axes[j]; V[:,j]=np.cross(axes[j],p-origins[j])
            return V,A
        inertial=body.find('inertial')
        if inertial is not None:
            mass=float(inertial.get('mass'))
            com=(T@np.r_[numbers(inertial.get('pos'),[0,0,0]),1])[:3]
            V,A=jac(com)
            I=T[:3,:3]@inertia_matrix(inertial)@T[:3,:3].T
            M+=mass*(V.T@V)+A.T@I@A
            g+=mass*9.81*V[2]
        for site in body.findall('site'):
            if site.get('name')=='push_tip':
                p=(T@np.r_[numbers(site.get('pos'),[0,0,0]),1])[:3]
                V,A=jac(p); found.update(tip=p,J=V,Jr=A,R=T[:3,:3])
        for child in body.findall('body'): visit(child,T,axes,origins)
    visit(xmlroot.find("worldbody/body[@name='link0']"),np.eye(4),{},{})
    found.update(M=M,g=g)
    return found

# Exact Fourier-polynomial matrix algebra on the q2/q4/q6 plane.
# All seven derivatives are carried to obtain physical gravity coefficients.
ZERO=(0,0,0)
def clean(p): return {k:v for k,v in p.items() if np.max(np.abs(v))>2e-12}
def padd(a,b):
    out={k:v.copy() for k,v in a.items()}
    for k,v in b.items(): out[k]=out.get(k,np.zeros_like(v))+v
    return clean(out)
def pmatmul(a,b):
    out={}
    for ka,va in a.items():
        for kb,vb in b.items():
            k=tuple(x+y for x,y in zip(ka,kb)); v=va@vb
            out[k]=out.get(k,np.zeros_like(v))+v
    return clean(out)
def rotation_poly(j):
    if j not in (1,3,5):
        R=np.eye(4); R[:3,:3]=rz(HOME[j])
        D=np.zeros((4,4)); D[:3,:3]=rz(HOME[j])@np.array([[0,-1,0],[1,0,0],[0,0,0.]])
        return {ZERO:R.astype(complex)},{ZERO:D.astype(complex)}
    axis=(1,3,5).index(j)
    kp=tuple(int(i==axis) for i in range(3)); km=tuple(-x for x in kp)
    A=np.zeros((4,4),dtype=complex); A[:2,:2]=[[.5,.5j],[-.5j,.5]]
    C=np.zeros((4,4),dtype=complex); C[2,2]=C[3,3]=1
    return {ZERO:C,kp:A,km:A.conj()},{kp:1j*A,km:-1j*A.conj()}
def planar_fourier(xmlroot):
    gravity={}; tip={}
    def visit(body,parent,parent_d):
        nonlocal gravity,tip
        F={ZERO:fixed_transform(body).astype(complex)}
        T=pmatmul(parent,F); D=[pmatmul(d,F) for d in parent_d]
        joint=body.find('joint')
        if joint is not None:
            j=int(joint.get('name').replace('joint',''))-1
            R,DR=rotation_poly(j)
            D=[padd(pmatmul(d,R),pmatmul(T,DR) if i==j else {}) for i,d in enumerate(D)]
            T=pmatmul(T,R)
        e=body.find('inertial')
        if e is not None:
            com=np.r_[numbers(e.get('pos'),[0,0,0]),1]; mg=float(e.get('mass'))*9.81
            for j,d in enumerate(D):
                for k,v in d.items():
                    if k not in gravity: gravity[k]=np.zeros(7,dtype=complex)
                    gravity[k][j]+=mg*(v[2]@com)
        for site in body.findall('site'):
            if site.get('name')=='push_tip':
                loc=np.r_[numbers(site.get('pos'),[0,0,0]),1]
                tip={k:(v@loc)[:3] for k,v in T.items()}
        for child in body.findall('body'): visit(child,T,D)
    visit(xmlroot.find("worldbody/body[@name='link0']"),{ZERO:np.eye(4,dtype=complex)},[{} for _ in range(7)])
    return clean(tip),clean(gravity)
def peval(poly,angles):
    return sum(v*np.exp(1j*np.dot(k,angles)) for k,v in poly.items()).real

class Expr:
    def __init__(self,terms=None,bias=0.):
        self.terms={} if terms is None else {k:float(v) for k,v in terms.items() if v!=0}
        self.bias=float(bias)
    def __add__(self,other):
        other=as_expr(other); terms=dict(self.terms)
        for k,v in other.terms.items(): terms[k]=terms.get(k,0.)+v
        return Expr(terms,self.bias+other.bias)
    __radd__=__add__
    def __neg__(self): return self*-1
    def __sub__(self,other): return self+-as_expr(other)
    def __rsub__(self,other): return as_expr(other)+-self
    def __mul__(self,a):
        if not np.isscalar(a): raise TypeError('Affine scalar only')
        return Expr({k:v*a for k,v in self.terms.items()},self.bias*a)
    __rmul__=__mul__
    def __truediv__(self,a): return self*(1/a)
def as_expr(x): return x if isinstance(x,Expr) else Expr(bias=x)
def var(name): return Expr({name:1})
def dot(coefs,names): return sum((float(a)*var(n) for a,n in zip(coefs,names)),Expr())

class MLP:
    def __init__(self,n):
        self.names=['o'+str(i) for i in range(n)]; self.input_dim=n; self.layers=[]
    def add_layer(self,W,b,act):
        W,b=np.asarray(W,dtype=np.float64),np.asarray(b,dtype=np.float64)
        assert W.ndim==2 and b.shape==(W.shape[0],)
        assert W.shape[1]==(self.layers[-1][0].shape[0] if self.layers else self.input_dim)
        assert np.isfinite(W).all() and np.isfinite(b).all()
        self.layers.append((W,b,act))
    def affine(self,outputs,keep=()):
        exprs={n:var(n) for n in keep}; exprs.update(outputs)
        W=np.zeros((len(exprs),len(self.names))); b=np.zeros(len(exprs))
        idx={n:i for i,n in enumerate(self.names)}
        for i,e in enumerate(exprs.values()):
            e=as_expr(e); b[i]=e.bias
            for n,a in e.terms.items(): W[i,idx[n]]+=a
        self.add_layer(W,b,'linear'); self.names=list(exprs)
    def stage(self): return Stage(self)
    def bounded_torques(self,expressions,limits):
        # Exact stable clipping, avoiding subtraction of two huge ReLUs.
        idx={n:i for i,n in enumerate(self.names)}
        W=np.zeros((len(expressions),len(self.names))); b=np.zeros(len(expressions))
        for i,(e,L) in enumerate(zip(expressions,limits)):
            e=as_expr(e); b[i]=1-e.bias/L
            for n,a in e.terms.items(): W[i,idx[n]]-=a/L
        self.add_layer(W,b,'relu')
        self.add_layer(-np.eye(len(expressions)),np.full(len(expressions),2.),'relu')
        self.add_layer(np.eye(len(expressions)),np.full(len(expressions),-1.),'linear')
        self.names=['output'+str(i) for i in range(len(expressions))]
    def save(self,path):
        arrays={'activations':np.array([a for _,_,a in self.layers],dtype='<U6')}
        for i,(W,b,_) in enumerate(self.layers): arrays['w'+str(i)]=W; arrays['b'+str(i)]=b
        assert 1<=len(self.layers)<=64
        assert max(max(W.shape) for W,_,_ in self.layers)<=4096
        assert sum(a.nbytes for a in arrays.values())<128*1024**2
        np.savez(path,**arrays)
    def evaluate(self,x):
        x=np.asarray(x,dtype=float)
        for W,b,a in self.layers:
            x=x@W.T+b
            if a=='relu': x=np.maximum(x,0)
            elif a=='tanh': x=np.tanh(x)
        return x
    def architecture(self): return [self.input_dim]+[W.shape[0] for W,_,_ in self.layers]

class Stage:
    def __init__(self,mlp): self.mlp=mlp; self.features=[]; self.cache={}
    def relu(self,e):
        e=as_expr(e); assert all(n in self.mlp.names for n in e.terms)
        key=(tuple(sorted(e.terms.items())),e.bias)
        if key not in self.cache:
            self.cache[key]='@'+str(len(self.features)); self.features.append(e)
        return var(self.cache[key])
    def clip(self,e,lo,hi): return lo+self.relu(e-lo)-self.relu(e-hi)
    def pwl(self,e,knots,values):
        # Hinge expansion of an analytic scalar linear interpolant, not fitting.
        t,y=np.asarray(knots),np.asarray(values); m=np.diff(y)/np.diff(t)
        coeff=np.r_[m[0],np.diff(m),-m[-1]]
        return float(y[0])+sum((float(a)*self.relu(e-float(k)) for a,k in zip(coeff,t) if abs(a)>1e-16),Expr())
    def product(self,a,b,bound_a,bound_b,segments=256):
        u,v=as_expr(a)/bound_a,as_expr(b)/bound_b; knots=np.linspace(-2.,2.,segments+1)
        return (bound_a*bound_b/4)*(self.pwl(u+v,knots,knots**2)-self.pwl(u-v,knots,knots**2))
    def finish(self,outputs,keep=()):
        mlp=self.mlp; n=len(mlp.names); idx={name:i for i,name in enumerate(mlp.names)}
        W=np.zeros((2*n+len(self.features),n)); b=np.zeros(W.shape[0])
        W[:n]=np.eye(n); W[n:2*n]=-np.eye(n)
        for i,e in enumerate(self.features,2*n):
            b[i]=e.bias
            for name,a in e.terms.items(): W[i,idx[name]]+=a
        mlp.add_layer(W,b,'relu')
        exprs={name:var(name) for name in keep}; exprs.update(outputs)
        V=np.zeros((len(exprs),W.shape[0])); c=np.zeros(len(exprs))
        for i,e in enumerate(exprs.values()):
            e=as_expr(e); c[i]=e.bias
            for name,a in e.terms.items():
                if name.startswith('@'): V[i,2*n+int(name[1:])]+=a
                else: V[i,idx[name]]+=a; V[i,n+idx[name]]-=a
        mlp.add_layer(V,c,'linear'); mlp.names=list(exprs)


def positive_key(k): return next((x for x in k if x),0)>0
def fourier_expr(poly,index):
    e=Expr(bias=poly[ZERO][index].real if ZERO in poly else 0.)
    for k,v in poly.items():
        if positive_key(k):
            tag='_'.join(map(str,k))
            e+=2*v[index].real*var('cos_'+tag)-2*v[index].imag*var('sin_'+tag)
    return e

# Fixed, directly selected geometry/control constants, not searched.
STEERING_GAIN = .5
STEERING_LIMIT = .015       # m: 60% of cube half-side
VELOCITY_LEAD_LIMIT = .020  # m: bound transient feed-forward
LATERAL_LIMIT = .080        # m: within the table's +/- .28 m half-width


def modal_design(static):
    # Modal damping from physical static inertia and designed stiffness only.
    J,Jr,M=static['J'],static['Jr'],static['M']; K=np.zeros((7,7))
    for j,k in ((0,70.),(2,90.),(4,30.)): K[j,j]+=k
    yaw_vector=np.zeros(7); yaw_vector[6]=1.; yaw_vector[0]=-1.
    K+=20.*np.outer(yaw_vector,yaw_vector)
    even=[1,3,5]; Jplanar=np.array([J[0,even],J[2,even],Jr[1,even]])
    K[np.ix_(even,even)]+=Jplanar.T@np.diag([220.,800.,35.])@Jplanar
    eig,U=np.linalg.eigh(M); Mh=(U*np.sqrt(eig))@U.T; Mi=(U*(1/np.sqrt(eig)))@U.T
    freq2,V=np.linalg.eigh(Mi@K@Mi)
    Dtotal=1.8*Mh@((V*np.sqrt(np.maximum(freq2,0)))@V.T)@Mh
    d,Q=np.linalg.eigh(Dtotal-np.eye(7)); Dfeedback=(Q*np.maximum(d,.2))@Q.T
    # Static low-frequency reference-to-tip lag. At equilibrium a unit base
    # yaw reference moves joints 1 and 7 together. Expand the physical damped
    # stiffness response algebraically at zero frequency; no time integration.
    yaw_mode=np.zeros(7); yaw_mode[[0,6]]=1.
    Dactual=Dfeedback+np.eye(7)  # Include XML joint damping.
    lag=float(J[1]@np.linalg.solve(K,Dactual@yaw_mode)/(J[1]@yaw_mode))
    assert lag>0 and np.isfinite(lag)
    return K,Dfeedback,eig,freq2,lag


def compile_franka(xmlroot,static,tip_poly,gravity_poly):
    K,Dfeedback,eig,freq2,lateral_lag=modal_design(static)
    velocity_lead=(1.+STEERING_GAIN)*lateral_lag
    net=MLP(29)
    qnames=['o'+str(i) for i in range(7)]; vnames=['o'+str(i) for i in range(7,14)]
    keys=sorted({k for p in (tip_poly,gravity_poly) for k in p if positive_key(k)})
    joint_bounds=np.array([[-1.7628,1.7628],[-3.0718,-.0698],[-.0175,3.7525]])
    st=net.stage(); trig={}
    for k in keys:
        a=dot(k,[qnames[i] for i in (1,3,5)])+float(np.dot(k,HOME[[1,3,5]]))
        lo=sum(min(c*x,c*y) for c,(x,y) in zip(k,joint_bounds))-.10
        hi=sum(max(c*x,c*y) for c,(x,y) in zip(k,joint_bounds))+.10
        knots=np.linspace(lo,hi,int(np.ceil((hi-lo)/.02))+1)
        tag='_'.join(map(str,k))
        trig['sin_'+tag]=st.pwl(a,knots,np.sin(knots))
        trig['cos_'+tag]=st.pwl(a,knots,np.cos(knots))
    # Position steering goes to the far side of the cube's lateral error.
    # The old command followed a moving cube with ~0.293 s tip lag. Reference
    # derivative is (1+k)*cube_vy in the unsaturated steering region, hence
    # the model-derived lead coefficient (1+k)*lag, with no differencing.
    cy=.2*var('o15'); gy=.2*var('o28'); vy=var('o22')
    trig['lateral_raw']=cy+st.clip(STEERING_GAIN*(cy-gy),-STEERING_LIMIT,STEERING_LIMIT)+st.clip(velocity_lead*vy,-VELOCITY_LEAD_LIMIT,VELOCITY_LEAD_LIMIT)
    st.finish(trig,keep=qnames+vnames+['o27'])
    out={'px':fourier_expr(tip_poly,0),'pz':fourier_expr(tip_poly,2)}
    for j in range(7): out['g'+str(j)]=fourier_expr(gravity_poly,j)
    for m,j in enumerate((1,3,5)):
        dpoly={k:1j*k[m]*v for k,v in tip_poly.items()}
        out['jx'+str(j)]=fourier_expr(dpoly,0); out['jz'+str(j)]=fourier_expr(dpoly,2)
    net.affine(out,keep=qnames+vnames+['o27','lateral_raw'])
    st=net.stage()
    out={'fx':st.clip(220.*(float(PUBLIC['tip_home'][0])+.2*var('o27')-.038-var('px')),-4.,4.),
         'fz':st.clip(800.*(float(PUBLIC['tip_home'][2])-var('pz')),-30.,30.),
         'pitch_moment':st.clip(-35.*(var('o1')-var('o3')-var('o5')),-8.,8.),
         'lateral_goal':st.clip(var('lateral_raw'),-LATERAL_LIMIT,LATERAL_LIMIT)}
    inv_grid=np.linspace(.28,.85,115)
    out['inverse_radius']=st.pwl(var('px'),inv_grid,1/inv_grid)
    keep=qnames+vnames+['g'+str(j) for j in range(7)]+['jx'+str(j) for j in (1,3,5)]+['jz'+str(j) for j in (1,3,5)]
    st.finish(out,keep=keep)
    st=net.stage(); out={}
    for j in (1,3,5):
        out['tx'+str(j)]=st.product(var('jx'+str(j)),var('fx'),1.1,4.,256)
        out['tz'+str(j)]=st.product(var('jz'+str(j)),var('fz'),1.1,30.,256)
    out['yaw_goal']=st.product(var('lateral_goal'),var('inverse_radius'),LATERAL_LIMIT,3.6,256)
    st.finish(out,keep=qnames+vnames+['g'+str(j) for j in range(7)]+['pitch_moment'])
    G=np.array(PUBLIC['gravity_gradient']); odd=[0,2,4,6]; even=[1,3,5]; torques=[]
    for j in range(7):
        t=var('g'+str(j))+dot(G[j,odd],[qnames[i] for i in odd])-3.*dot(Dfeedback[j],vnames)
        if j in even:
            t+=var('tx'+str(j))+var('tz'+str(j))
            t+=(1. if j==1 else -1.)*var('pitch_moment')
        elif j==0: t+=70.*(var('yaw_goal')-var('o0'))+20.*(var('o6')-var('o0'))
        elif j==2: t-=90.*var('o2')
        elif j==4: t-=30.*var('o4')
        elif j==6: t-=20.*(var('o6')-var('o0'))
        torques.append(t)
    net.bounded_torques(torques,PUBLIC['torque_limits'])
    design={'lateral_lag_seconds':lateral_lag,'velocity_lead_seconds':velocity_lead,
            'steering_gain':STEERING_GAIN,'steering_limit_m':STEERING_LIMIT,
            'velocity_lead_limit_m':VELOCITY_LEAD_LIMIT,'lateral_limit_m':LATERAL_LIMIT,
            'fourier_keys':keys,'home_mass_eigenvalues':eig.tolist(),
            'undamped_home_modal_frequencies':np.sqrt(freq2).tolist(),
            'feedback_damping':Dfeedback.tolist(),
            'max_sine_cosine_interpolation_error_bound':.02**2/8}
    return net,design

def validate_artifact(path,inputs):
    # Independent synthetic input checks; no transitions, times or rewards.
    with np.load(path,allow_pickle=False) as data:
        acts=data['activations']; assert acts.dtype.kind=='U'
        assert set(data.files)=={'activations'}|{p+str(i) for i in range(len(acts)) for p in ('w','b')}
        x=inputs.copy()
        for i,a in enumerate(acts):
            W,b=data['w'+str(i)],data['b'+str(i)]
            assert W.ndim==2 and b.shape==(W.shape[0],)
            assert W.dtype.kind=='f' and b.dtype.kind=='f'
            assert np.isfinite(W).all() and np.isfinite(b).all()
            x=x@W.T+b
            if a=='relu': x=np.maximum(x,0)
            elif a=='tanh': x=np.tanh(x)
            else: assert a=='linear'
        assert np.isfinite(x).all()
        assert np.all(x>=-1.) and np.all(x<=1.)
    return {'layers':len(acts),'bytes':Path(path).stat().st_size,
            'output_min':float(x.min()),'output_max':float(x.max())}


def main():
    # Static XML algebra only; never load a simulator or advance any state.
    xmlroot=ET.parse(ROOT/'franka_push.xml').getroot()
    static=static_home_model(xmlroot)
    np.testing.assert_allclose(static['tip'],PUBLIC['tip_home'],atol=2e-12)
    np.testing.assert_allclose(static['J'],PUBLIC['jacobian'],atol=2e-12)
    np.testing.assert_allclose(static['Jr'],PUBLIC['rotation_jacobian'],atol=2e-12)
    np.testing.assert_allclose(static['g'],PUBLIC['gravity_home'],atol=2e-10)
    tip_poly,g_poly=planar_fourier(xmlroot)
    np.testing.assert_allclose(peval(tip_poly,HOME[[1,3,5]]),static['tip'],atol=2e-12)
    np.testing.assert_allclose(peval(g_poly,HOME[[1,3,5]]),static['g'],atol=2e-10)
    net,design=compile_franka(xmlroot,static,tip_poly,g_poly)
    output=BASE/'franka_push.npz'
    net.save(output)

    # Independent synthetic states: finite outputs/schema checks, not rollout,
    # optimization, teacher fitting, or evaluation of success.
    rng=np.random.default_rng(20261105)
    fi=np.zeros((256,29))
    limits=np.array([[-2.8973,2.8973],[-1.7628,1.7628],[-2.8973,2.8973],
                     [-3.0718,-.0698],[-2.8973,2.8973],[-.0175,3.7525],
                     [-2.8973,2.8973]])
    fi[:,:7]=rng.uniform(limits[:,0],limits[:,1],(256,7))-HOME
    fi[:,7:14]=rng.uniform(-2,2,(256,7))
    fi[:,14]=rng.uniform(.04,.23,256)/.2
    fi[:,15]=rng.uniform(-.09,.09,256)/.2
    fi[:,16]=rng.uniform(-.01,.02,256)/.2
    fi[:,17:21]=rng.normal(size=(256,4))
    fi[:,17:21]/=np.linalg.norm(fi[:,17:21],axis=1,keepdims=True)
    fi[:,21:24]=rng.uniform(-.12,.12,(256,3))
    fi[:,24:27]=rng.uniform(-1,1,(256,3))
    fi[:,27]=rng.uniform(.14,.18,256)/.2
    fi[:,28]=rng.uniform(-.015,.015,256)/.2
    fi=np.vstack([fi,np.zeros((1,29)),np.full((1,29),1e4),np.full((1,29),-1e4)])
    checks=validate_artifact(output,fi)

    # The first ReLU/affine pair must reproduce the intended reference exactly.
    W,b,_=net.layers[0]; h=np.maximum(fi@W.T+b,0)
    W,b,_=net.layers[1]; first=h@W.T+b
    expected=.2*fi[:,15]+np.clip(STEERING_GAIN*.2*(fi[:,15]-fi[:,28]),-STEERING_LIMIT,STEERING_LIMIT)+np.clip(design['velocity_lead_seconds']*fi[:,22],-VELOCITY_LEAD_LIMIT,VELOCITY_LEAD_LIMIT)
    np.testing.assert_allclose(first[:,-1],expected,atol=1e-10,rtol=1e-12)
    checks['lateral_reference_max_abs_error']=float(np.max(np.abs(first[:,-1]-expected)))
    checks['architecture']=net.architecture()
    checks['activations']=[a for _,_,a in net.layers]
    checks['parameters']=sum(W.size+b.size for W,b,_ in net.layers)

    rationale="""FRANKA-ONLY DIRECT-WEIGHT REVISION

SCOPE AND EVIDENCE
The original Franka policy scored 8/10 in the supplied, counted development
set. All ten JSON reports and numeric NPZ recordings were inspected. No new
trajectories, simulations, integrations, reward calls, training, gradients,
parameter sweeps, optimization, fitted teachers, or external data were used.
Recorded poses were converted to tip positions with static XML kinematics.
The previous source reconstructed the accepted NPZ arrays exactly. No pendulum
policy was constructed, modified, or submitted in this revision.

Both failures (0000, 0003) were timeouts dominated by lateral drift, not lack
of forward progress or torque saturation. At the last recorded pre-step
boundary, their cube-minus-target xy errors in millimetres were approximately
[-3.97, +69.16] and [0.00, -47.49]. Rotated footprint containment margins in
millimetres were [35.85, -29.34] and [34.06, -13.43]. Their longest reported
holds were 0 and 0.036 seconds. The cubes remained near table height.

The old lateral command was clip(cy + 0.15*(cy-gy), -0.04, 0.04). It followed
lateral cube motion too slowly and then hit its absolute cap. In case 0000
at 1.5 s, the intended offset was +5.48 mm but the actual tip was 5.4 mm BELOW
the cube. In case 0003 at 2.0 s, the intended offset was -2.77 mm but the tip
was 10.0 mm ABOVE the cube. Thus actual contact approached from the opposite
side to the commanded corrective bias. Once the cube drifted beyond the cap,
the command could not move the pusher around its far side. This supports a
lateral-reference correction; it does not establish its behavioral outcome.

CHOSEN CORRECTION (one fixed design, no candidate search)
Keep the physical controller and forward command, changing only the lateral
reference. Let cy=0.2*observation[15], gy=0.2*observation[28], and
vy=observation[22]. All y positions here are relative to tip_home.y.

  steering = clip(0.5*(cy-gy), -0.015, 0.015) metres
  lead     = clip(Tlead*vy, -0.020, 0.020) metres
  lateral_goal = clip(cy + steering + lead, -0.080, 0.080) metres

The steering offset is still toward the far side of the target error, but
is stronger and separately bounded. Worst-yaw horizontal cube half-extent
is 0.025*sqrt(2), leaving a center tolerance of 0.029645 m. Gain 0.5 gives
approximately a 15 mm offset at that tolerance. The offset cap is 60% of the
cube half-side; this is a conservative geometric scale, not a contact proof.

Lead comes from the unchanged static mass/stiffness/damping design, not a fit
to trajectories. Let K be the home stiffness, D the feedback damping plus XML
joint damping, J_y the home tip Jacobian row, and a=[1,0,0,0,0,0,1]. The
zero-frequency reference-to-tip lag is
  Tlag = (J_y K^(-1) D a)/(J_y a).
Because the unsaturated reference has derivative (1+0.5)*vy, use
Tlead=1.5*Tlag. The lead is clipped to 20 mm to limit noisy/contact transients;
observations are exact, but impact velocities can still change abruptly.
The new 80 mm absolute cap covers the valid-center lateral band plus both
bounded offsets (15+29.645+15+20 = 79.645 mm), and is far inside the table's
280 mm lateral half-width. It removes the observed +/-40 mm command trap.
Only the lateral product's normalization bound changes to match the new cap.

PRESERVED CONSTRUCTION
Gravity and planar q2/q4/q6 kinematics are analytic Fourier polynomials of
the XML chain. Sines/cosines use directly sampled analytic piecewise-linear
interpolants (angle spacing <=0.02 rad), not fitted data. Odd-joint gravity
corrections use the public gravity gradient. Forward force remains a 220 N/m
spring to target_x-0.038 m, capped at 4 N. Vertical stiffness stays 800 N/m;
pitch stiffness stays 35 Nm/rad. Joint posture gains and static modal damping
are unchanged. Products use the original piecewise-linear square identity.
The network receives all 29 observations and emits seven normalized torques.

OUTPUT CONTRACT AND STATIC VALIDATION
Every layer is an ordinary finite float64 affine map followed by linear or
ReLU activation. The only NPZ keys are activations (Unicode), w0,b0,w1,b1,... .
There is no custom code, state, observation preprocessor, or gravity servo at
runtime. The final three layers compute
  -1 + ReLU(2 - ReLU(1 - torque/limit)),
which bounds every finite unsaturated command in [-1,1] inside the network.
Static home identities, schema/shape/size checks, finite/bounded outputs on
259 independent synthetic inputs, and the algebraic lateral-reference
identity were checked. None of these checks propagates states or tests success.

LIMITATIONS
The revision has not had any behavioral evaluation. The independent counted
evaluation must determine whether it improves or harms the original 8/10.
Ten of the 100 development episodes have been used; this session used zero
additional episodes. Lead is only a low-frequency home linearization and
omits acceleration; clipping changes its compensation near limits. Finger/
cube friction, face changes, rotated contact geometry, and delays may still
cause drift or contact loss. The widened cap increases possible off-plane
motion. Planar kinematics plus first-order off-plane gravity, omitted Coriolis
terms, target-only forward motion, and the absence of orientation/contact
recovery logic remain inherited limitations. No success rate is claimed.

ACTUAL ARTIFACT
"""
    rationale+=f"Tlag = {design['lateral_lag_seconds']:.12f} s; Tlead = {design['velocity_lead_seconds']:.12f} s.\n"
    rationale+='Architecture (input then affine output widths): '+str(checks['architecture'])+'\n'
    rationale+='Activations: '+', '.join(checks['activations'])+'\n'
    rationale+=f"{checks['parameters']} float parameters; {checks['layers']} layers; {checks['bytes']} file bytes.\n"
    rationale+=f"Synthetic output range: [{checks['output_min']}, {checks['output_max']}].\n"
    rationale+=f"Lateral identity max absolute error: {checks['lateral_reference_max_abs_error']:.3g} m.\n"
    (BASE/'rationale.txt').write_text(rationale)
    print(json.dumps({'checks':checks,'static_design':design,
                      'new_rollouts':0,'optimization_steps':0},indent=2))


if __name__=='__main__':
    main()
