--- /mnt/data/nex_v300_grip_entry_contract/NEXT11_V300_GRIP_ENTRY_CONTRACT_REHYDRATED_ROOT/assets/nexprod/nexprod01-runtime-candidate.mjs 2026-08-19 18:23:13.928579977 +0000 +++ /mnt/data/nex_v301_side_surface_gate/NEXT11_V301_SIDE_SURFACE_CONTACT_GATE_ROOT/assets/nexprod/nexprod01-runtime-candidate.mjs 2026-08-19 18:34:53.072948130 +0000 @@ -21,7 +21,7 @@ sourceDerivedCanonicalGateForSpec } from './nexprod01-core.mjs'; -export const NEXPROD01_RUNTIME_VERSION = 'NEXPROD01_RUNTIME_V300_GRIP_ENTRY_CONTRACT_REHYDRATED_2026_08_19'; +export const NEXPROD01_RUNTIME_VERSION = 'NEXPROD01_RUNTIME_V301_SIDE_SURFACE_CONTACT_GATE_2026_08_19'; export const UNIT_SCALE = 0.010162; export const FOCUS_CASES = [ @@ -3006,10 +3006,10 @@ const first=gripV300CapsuleSurfaceMetrics(pA,limb), second=gripV300CapsuleSurfaceMetrics(pB,limb); const basis=gripV1HandBasis(out,b); const palmFacesObject=dot(basis.z,scaleP(limb.outward,-1)); - const thumbDownProxy=dot(basis.x,{x:0,y:-1,z:0}); + const handAcrossVerticalProxy=dot(basis.x,{x:0,y:-1,z:0}); const grossPen=first.penetrationM>0.018 || second.penetrationM>0.018; const score=(firstErr*5000)+(second.distanceToSurfaceM*120)+(second.sideOk?0:50)+(grossPen?100:0)+(palmFacesObject>0?0:10)+Math.abs(deg)*0.002; - const cand={deg,score,arm,firstErrM:+firstErr.toFixed(5),first,second,palmFacesObject:+palmFacesObject.toFixed(5),thumbDownProxy:+thumbDownProxy.toFixed(5),grossPenetration:grossPen,pointA:roundObj(pA),pointB:roundObj(pB)}; + const cand={deg,score,arm,firstErrM:+firstErr.toFixed(5),first,second,palmFacesObject:+palmFacesObject.toFixed(5),handAcrossVerticalProxy:+handAcrossVerticalProxy.toFixed(5),grossPenetration:grossPen,pointA:roundObj(pA),pointB:roundObj(pB)}; if(!best || cand.score0); - // Thumb-down is a real contract gate, but V300 does not write thumb bones and does not yet have an approved actual thumb-axis probe. - // Therefore the contact geometry can be ready while finger release remains blocked until thumb-down is visually/probe verified. - const thumbDownVerified=false; - const preFingerReady=!!(palmContactReady && thumbDownVerified); - return {schema:'NEXPROD01_V300_PALM_FRAME_GATE',active:true,pass:preFingerReady,palmContactReady,preFingerReady,status:preFingerReady?'GRIP_ENTRY_CONTRACT_PREFINGER_READY':(palmContactReady?'PALM_CONTACT_READY_THUMB_DOWN_GATE_PENDING':'GRIP_ENTRY_CONTRACT_HOLD_BEFORE_FINGERS'),checks:{handPalmTowardObject:roll?.after?.palmFacesObject!==undefined ? roll.after.palmFacesObject>0 : null,thumbDownVerified,thumbDownGate:'UNVERIFIED_THUMB_AXIS_NO_THUMB_WRITE_IN_V300',thumbDownProxyTelemetry:roll?.after?.thumbDownProxy??null,firstPalmOnShiftedSurface:first?.surfaceOk??null,secondPalmOnShiftedSurface:second?.surfaceOk??null,outsideContact:!!(first?.sideOk && second?.sideOk),noGrossPenetration:roll?.after?.grossPenetration===false,objectMeetWithinLimit:objectMeet?.pass??null},policy:'V300 implements the recovered five-step grip-entry contract. Palm contact may be solved, but the thumb-down gate is explicitly pending because no approved thumb-axis writer/probe is active. Fingers remain blocked.'}; + const handPalmTowardObject=roll?.after?.palmFacesObject!==undefined ? roll.after.palmFacesObject>0 : false; + const firstPalmOnShiftedSurface=!!first?.surfaceOk; + const secondPalmOnShiftedSurface=!!second?.surfaceOk; + const outsideContact=!!(first?.sideOk && second?.sideOk); + const noGrossPenetration=roll?.after?.grossPenetration===false; + const objectMeetWithinLimit=!!objectMeet?.pass; + const sideSurfaceReady=!!(objectMeetWithinLimit && roll?.pass && handPalmTowardObject && firstPalmOnShiftedSurface && secondPalmOnShiftedSurface && outsideContact && noGrossPenetration); + return {schema:'NEXPROD01_V301_SIDE_SURFACE_CONTACT_GATE',active:true,pass:sideSurfaceReady,palmContactReady:sideSurfaceReady,preFingerReady:sideSurfaceReady,status:sideSurfaceReady?'GRIP_ENTRY_CONTRACT_PREFINGER_READY':'GRIP_ENTRY_CONTRACT_HOLD_WRONG_SIDE_OR_PENETRATION',checks:{handPalmTowardObject,firstPalmOnShiftedSurface,secondPalmOnShiftedSurface,outsideContact,noGrossPenetration,objectMeetWithinLimit,firstSignedOutsideM:first?.signedOutsideM??null,secondSignedOutsideM:second?.signedOutsideM??null,firstPenetrationM:first?.penetrationM??null,secondPenetrationM:second?.penetrationM??null,firstOutsideScore:first?.outsideScore??null,secondOutsideScore:second?.outsideScore??null},policy:'V301 replaces the explicit thumb-down requirement with a generic side/surface/penetration gate. The solver only checks that palm-side contact is on the intended object side, both palm points remain on the shifted outside surface, the palm faces the object, and the hand has not snapped through or penetrated the object. Thumb orientation is an emergent result of correct side contact, not a global constraint.'}; + } + + function v301FingerChildLayerGate(side,gripV1){ + const contract=gripV1?.audit||{}; + const sideGate=contract?.stages?.v301SideSurfaceContactGate||null; + const releaseEnabled=globalThis.__NEXPROD01_ENABLE_V301_FINGER_CHILD_LAYER===true; + if(contract.pass && releaseEnabled){ + return {schema:'NEXPROD01_V301_FINGER_CHILD_LAYER_GATE',active:true,blocked:false,ready:true,releaseEnabled:true,side,reason:'side_surface_gate_passed_explicit_runtime_flag_enabled',contractStatus:contract.status,sideGate,policy:'Fingers may run only after the side/surface/penetration gate passes and an explicit integration flag enables child-layer writes.'}; + } + return {schema:'NEXPROD01_V301_FINGER_CHILD_LAYER_GATE',active:false,blocked:true,ready:!!contract.pass,releaseEnabled:false,side,reason:contract.pass?'side_surface_gate_passed_finger_release_deferred_to_V302_child_only_integration':'side_surface_gate_not_passed',contractStatus:contract.status||null,sideGate,policy:'V301 verifies the generic side/surface contact gate but does not yet release finger writes by default. No finger, thumb, hand, wrist, arm, snap-point, leg, foot, or toe writes are made by this deferred gate.'}; } function gripV1PalmContactConfig(side){ @@ -3055,8 +3067,8 @@ const b=gripV1Branch(side); const out={...(targets||{})}; const rule=gripV1ChooseRule(sourceContact,side); - const audit={schema:'NEXPROD01_GRIP_SOLVER_V1',version:'V300_GRIP_ENTRY_CONTRACT_REHYDRATED_FROM_V267',side,applied:false,implemented:true, - policy:'V300 rehydrated clean grip solver: V267 palm-contract owner plus explicit five-step grip-entry contract. Neutralization is an approach/contact gate; object/leg meet uses shifted-surface validation; fingers remain blocked until separate visual approval.'}; + const audit={schema:'NEXPROD01_GRIP_SOLVER_V1',version:'V301_SIDE_SURFACE_CONTACT_GATE_FROM_V300',side,applied:false,implemented:true, + policy:'V301 side-surface grip solver: V300 palm/contact owner plus generic right-side/outside-surface and penetration validation. Thumb direction is not a gate; it follows from correct palm-side contact geometry. Finger writes remain deferred by default.'}; if(!rule) return {targets:out,audit:{...audit,implemented:false,reason:'no_nonfloor_grip_rule_for_side'}}; const required=[b.U,b.E,b.H,b.M,b.I,b.P,b.F,b.K,b.T]; if(!required.every(k=>out[k])) return {targets:out,audit:{...audit,reason:'missing_required_targets',missing:required.filter(k=>!out[k])}}; @@ -3081,10 +3093,10 @@ stages.v300ObjectMeetGate=gripV300ObjectMeetGate(armShift); if(!stages.v300ObjectMeetGate.pass) return {targets:out,audit:{...audit,reason:'object_meet_exceeds_v300_limit',stages,nextPhase:'stop_before_fingers_object_meet_limit'}}; stages.v300ForearmAxisSecondPalmRoll=gripV300ForearmRollSecondPalm(out,rawTargets,b,limb,cfg,{caseId:opts.caseId,phase:opts.phase}); - stages.v300PalmFrameGate=gripV300PalmFrameGate(stages); - if(stages.v300PalmFrameGate.palmContactReady){ - const afterV300=v93Basis(out,side); - return {targets:out,audit:{...audit,applied:true,pass:false,palmContactReady:!!stages.v300PalmFrameGate.palmContactReady,preFingerReady:false,status:stages.v300PalmFrameGate.status,ruleType:rule.type||null,targetLimb:rule.targetLimb||null,targetSide:'outside_shifted_surface',palmPoints:{pointA:cfg.pointA,pointB:cfg.pointB},limb:{axisA:limb.axisA,axisB:limb.axisB,axisPoint:roundObj(limb.axisPoint),outside:roundObj(limb.outward),target:roundObj(limb.target),radiusM:+limb.radius.toFixed(5),clearanceM:+limb.clearance.toFixed(5),t:+limb.t.toFixed(5)},beforeBasis:null,afterAngles:afterV300?.angles||null,stages,nextPhase:'thumb_down_gate_pending_finger_child_layer_blocked',notes:['V300 palm contact is solved, but thumb-down gate is pending; pass remains false so v68/V200 finger layer stays blocked','first palm contact was held while wrist/forearm-axis roll searched the second palm point','shifted-surface gate is evaluated after object/leg meet']}}; + stages.v301SideSurfaceContactGate=gripV301SideSurfaceContactGate(stages); + if(stages.v301SideSurfaceContactGate.palmContactReady){ + const afterV301=v93Basis(out,side); + return {targets:out,audit:{...audit,applied:true,pass:!!stages.v301SideSurfaceContactGate.pass,palmContactReady:!!stages.v301SideSurfaceContactGate.palmContactReady,preFingerReady:!!stages.v301SideSurfaceContactGate.preFingerReady,status:stages.v301SideSurfaceContactGate.status,ruleType:rule.type||null,targetLimb:rule.targetLimb||null,targetSide:'outside_shifted_surface',palmPoints:{pointA:cfg.pointA,pointB:cfg.pointB},limb:{axisA:limb.axisA,axisB:limb.axisB,axisPoint:roundObj(limb.axisPoint),outside:roundObj(limb.outward),target:roundObj(limb.target),radiusM:+limb.radius.toFixed(5),clearanceM:+limb.clearance.toFixed(5),t:+limb.t.toFixed(5)},beforeBasis:null,afterAngles:afterV301?.angles||null,stages,nextPhase:'finger_child_layer_ready_but_deferred_to_v302_integration',notes:['V301 side/surface/penetration gate passed without an explicit thumb-direction constraint','first palm contact was held while wrist/forearm-axis roll searched the second palm point','shifted-surface gate is evaluated after object/leg meet','finger writes remain deferred by default so V301 only changes contact gating']}}; } // Align the line between the two inside-palm points to the target limb axis. There is still one @@ -3237,11 +3249,13 @@ reset(); commit=applyOneWriterCommit(finalTargets,sourceContact); v93AfterCommitTargets=getActualTargets(); - if(v110Grip.audit?.pass){ + const v301FingerGate=v301FingerChildLayerGate(activeGripSide, gripV1); + if(v110Grip.audit?.pass && v301FingerGate.releaseEnabled){ v68ShinGripFingerLayer=v68ApplyShinGripFingerLayer(sourceContact, finalTargets); + v68ShinGripFingerLayer.v301FingerChildLayerGate=v301FingerGate; } else { - v68ShinGripFingerLayer=v297BlockedFingerChildLayer(activeGripSide, gripV1); - try{ globalThis.__NEXPROD01_V297_PREFINGER_CHILD_LAYER_GATE=v68ShinGripFingerLayer; }catch(e){} + v68ShinGripFingerLayer=v301FingerGate; + try{ globalThis.__NEXPROD01_V301_FINGER_CHILD_LAYER_GATE=v68ShinGripFingerLayer; globalThis.__NEXPROD01_V297_PREFINGER_CHILD_LAYER_GATE=v68ShinGripFingerLayer; }catch(e){} } commit.v68ShinGripFingerLayer=v68ShinGripFingerLayer; v140GripHandQaTint=v140InstallGripHandQaTint(activeGripSide); commit.v140GripHandQaTint=v140GripHandQaTint;