{
  "schema_version": "1",
  "id": "mujoco-dynamixel-2r-joint-control",
  "name": "dynamixel 2r joint-position control",
  "summary": "dynamixel 2r joint-position control. Bounded simulated joint offsets from the original reset pose; not locomotion or object-task validation.",
  "capabilities": [
    "controller",
    "joint-control",
    "simulation",
    "bounded-json"
  ],
  "aliases": [
    "dynamixel_2r",
    "menagerie",
    "joint_control"
  ],
  "source": {
    "repository": "https://github.com/google-deepmind/mujoco_menagerie",
    "commit": "62ab00f2bc44f6f20c1c9b7a13f3fb1dd41a4e35",
    "archive_url": "https://codeload.github.com/google-deepmind/mujoco_menagerie/tar.gz/62ab00f2bc44f6f20c1c9b7a13f3fb1dd41a4e35",
    "archive_sha256": "6866543ef9f07fd24f3fa4d134c06b30f5276f48daf412022b6edbaf6b2f0152",
    "archive_bytes": 520071141,
    "authors": [
      "MuJoCo Menagerie contributors and the pinned model licensors"
    ],
    "paths": [
      "dynamixel_2r/CHANGELOG.md",
      "dynamixel_2r/LICENSE",
      "dynamixel_2r/README.md",
      "dynamixel_2r/assets/base_mx106.stl",
      "dynamixel_2r/assets/dc05_m01_dummy.stl",
      "dynamixel_2r/assets/dc08_m01_dummy.stl",
      "dynamixel_2r/assets/first_segment_mix.stl",
      "dynamixel_2r/assets/hn05_i101.stl",
      "dynamixel_2r/assets/m2_5x12.stl",
      "dynamixel_2r/assets/m2_5x20.stl",
      "dynamixel_2r/assets/m2_5x6.stl",
      "dynamixel_2r/assets/m2_5x8.stl",
      "dynamixel_2r/assets/m4x25.stl",
      "dynamixel_2r/assets/mid_part.stl",
      "dynamixel_2r/assets/nut_2_5.stl",
      "dynamixel_2r/assets/nut_4.stl",
      "dynamixel_2r/assets/sw_mx64_segment.stl",
      "dynamixel_2r/assets/weight_985.stl",
      "dynamixel_2r/config.json",
      "dynamixel_2r/dynamixel_2r.png",
      "dynamixel_2r/dynamixel_2r.xml",
      "dynamixel_2r/scene.xml"
    ]
  },
  "license": {
    "spdx": "MIT",
    "url": "https://github.com/google-deepmind/mujoco_menagerie/blob/62ab00f2bc44f6f20c1c9b7a13f3fb1dd41a4e35/dynamixel_2r/LICENSE",
    "verified": true,
    "scope": "Original model-specific license and asset rights remain applicable. Independent Hub wrapper is MIT."
  },
  "compatibility": [
    {
      "runtime": "mujoco",
      "runtime_version": "3.3.7",
      "robot": "dynamixel_2r",
      "controller": "native_position_actuators",
      "observation": "joint_state",
      "os": "linux",
      "architecture": "x86_64",
      "python": "3.10",
      "status": "hub_verified"
    }
  ],
  "observation": {
    "required": [
      "simulator_state"
    ],
    "privileged_state": true,
    "description": "Privileged simulator state and declared goals; no perception or real sensor validation."
  },
  "requirements": {
    "python": ">=3.10,<3.11",
    "packages": [
      "mujoco==3.3.7",
      "numpy==1.26.4"
    ],
    "hardware": [
      "Linux x86_64; CPU simulation; see retained receipt for the exact host environment"
    ],
    "rendering": "Headless. EGL libraries are required for MuJoCo imports; PushT uses SDL dummy video. No interactive display.",
    "api_keys": [],
    "network": [
      "Pinned source archive during preparation",
      "PyPI dependencies during explicit installation"
    ],
    "dependency_lock": "Direct runtime versions are pinned and measured. The receipt retains the complete environment freeze; prepare records the installed environment. System drivers are separate host requirements."
  },
  "invocation": {
    "entrypoint": "python run.py",
    "argv": [
      "{python}",
      "{recipe_dir}/run.py",
      "--source-dir",
      "{source_dir}",
      "--example"
    ],
    "inputs": {
      "seed": 0,
      "joint_offset_rad": 0.08
    },
    "outputs": {
      "format": "single JSON object",
      "payload_selector": "outputs",
      "schema": {
        "type": "object",
        "required": [
          "joint_positions_rad",
          "target_joint_positions_rad",
          "max_error_rad",
          "movement_rad"
        ],
        "properties": {
          "joint_positions_rad": {
            "type": "array",
            "minItems": 2,
            "maxItems": 2,
            "items": {
              "type": "number",
              "minimum": -20,
              "maximum": 20
            },
            "x-unit": "rad",
            "x-frame": "named_model_joint_coordinates",
            "x-order": [
              "R2",
              "R1"
            ]
          },
          "target_joint_positions_rad": {
            "type": "array",
            "minItems": 2,
            "maxItems": 2,
            "items": {
              "type": "number",
              "minimum": -20,
              "maximum": 20
            },
            "x-unit": "rad",
            "x-frame": "named_model_joint_coordinates",
            "x-order": [
              "R2",
              "R1"
            ]
          },
          "max_error_rad": {
            "type": "number",
            "minimum": 0,
            "maximum": 50,
            "x-unit": "rad"
          },
          "movement_rad": {
            "type": "number",
            "minimum": 0,
            "maximum": 50,
            "x-unit": "rad"
          }
        },
        "additionalProperties": false
      }
    },
    "example": "Prepare explicitly; run --example or replace it with --input TASK.json. --describe reads the bounded contract without running simulation."
  },
  "interface": {
    "version": "1",
    "kind": "controller",
    "abstraction_level": 1,
    "execution_domain": "simulation",
    "language": "python",
    "input_transport": "json_file_or_inline",
    "output_transport": "json_stdout",
    "output_selector": "outputs",
    "conventions": {
      "linear_unit": "m",
      "angular_unit": "rad",
      "time_unit": "s",
      "coordinate_frame": "right_handed_world_xyz; +z_up",
      "joint_order": "R2,R1",
      "quaternion_order": "not_used"
    },
    "input_schema": {
      "type": "object",
      "required": [
        "seed",
        "joint_offset_rad"
      ],
      "properties": {
        "seed": {
          "type": "integer",
          "minimum": 0,
          "maximum": 10000
        },
        "joint_offset_rad": {
          "type": "number",
          "minimum": -0.15,
          "maximum": 0.15,
          "x-unit": "rad"
        }
      },
      "additionalProperties": false
    },
    "output_schema": {
      "type": "object",
      "required": [
        "joint_positions_rad",
        "target_joint_positions_rad",
        "max_error_rad",
        "movement_rad"
      ],
      "properties": {
        "joint_positions_rad": {
          "type": "array",
          "minItems": 2,
          "maxItems": 2,
          "items": {
            "type": "number",
            "minimum": -20,
            "maximum": 20
          },
          "x-unit": "rad",
          "x-frame": "named_model_joint_coordinates",
          "x-order": [
            "R2",
            "R1"
          ]
        },
        "target_joint_positions_rad": {
          "type": "array",
          "minItems": 2,
          "maxItems": 2,
          "items": {
            "type": "number",
            "minimum": -20,
            "maximum": 20
          },
          "x-unit": "rad",
          "x-frame": "named_model_joint_coordinates",
          "x-order": [
            "R2",
            "R1"
          ]
        },
        "max_error_rad": {
          "type": "number",
          "minimum": 0,
          "maximum": 50,
          "x-unit": "rad"
        },
        "movement_rad": {
          "type": "number",
          "minimum": 0,
          "maximum": 50,
          "x-unit": "rad"
        }
      },
      "additionalProperties": false
    },
    "preconditions": [
      "Use the exact recorded source, Linux Python 3.10 environment and pinned direct packages.",
      "No physical robot, network actuation or external robot credentials."
    ],
    "postconditions": [
      "All reported checks must be true and all output numbers finite.",
      "Success covers the declared goal and frozen cases, not arbitrary tasks or physical hardware."
    ],
    "failure_codes": [
      "invalid_input_or_environment",
      "execution_failed",
      "task_failed"
    ],
    "composition": {
      "depends_on": [],
      "automatic_frame_conversion": false,
      "automatic_hardware_execution": false
    },
    "origin_reference_ids": [
      "mujoco-menagerie-artifact-dynamixel-2r-scene-xml"
    ],
    "origin_relationship": "prepared_from_the_same_pinned_model; inspect the declared controller and goal; no physical compatibility is inferred"
  },
  "completion": {
    "success_condition": "All declared goal and finite-state checks pass; exit 0 with task_complete. Move the named actuated hinge joints by a bounded offset from the reset/settled pose; max residual <0.035 rad and nonzero actual motion. This is not end-effector planning, balance, locomotion or hardware control.",
    "timeout_seconds": 180,
    "failure_modes": [
      "Invalid input or source checksum",
      "Different pinned package version",
      "Unavailable runtime/driver",
      "Goal not reached within bounded steps"
    ],
    "stop_condition": "Terminate the adapter at the recorded timeout. No physical hardware is opened."
  },
  "preparation": {
    "recipe_path": "recipes/mujoco-dynamixel-2r-joint-control",
    "requirements_file": "recipes/mujoco-dynamixel-2r-joint-control/requirements.txt",
    "source_selection": "declared_paths",
    "install_argv": [
      "{python}",
      "-m",
      "pip",
      "install",
      "--only-binary=:all:",
      "-r",
      "{recipe_dir}/requirements.txt"
    ],
    "execution_separate": true,
    "files": {
      "run.py": "# Hub-authored adapter: MIT. Upstream source/models retain their original licenses.\n\"\"\"Source template for standalone, bounded JSON recipe adapters (MIT, Hub authors).\n\nThe generator embeds this template and a reviewed backend in each published\nrecipe. Importing this template does not import or execute upstream source.\n\"\"\"\nimport argparse\nimport contextlib\nimport hashlib\nimport importlib.util\nimport io\nimport json\nimport math\nimport os\nfrom pathlib import Path\nimport random\nimport sys\n\n\nclass ContractError(ValueError):\n    pass\n\n\ndef validate_data(value, schema, path='$'):\n    \"\"\"Supported, published JSON Schema subset; bool is never a number.\"\"\"\n    kind = schema.get('type')\n    checks = {'object': lambda: isinstance(value, dict), 'array': lambda: isinstance(value, list),\n              'number': lambda: type(value) in (int, float) and math.isfinite(value),\n              'integer': lambda: type(value) is int, 'string': lambda: isinstance(value, str),\n              'boolean': lambda: isinstance(value, bool), 'null': lambda: value is None}\n    if kind and (kind not in checks or not checks[kind]()):\n        raise ContractError(path + ': expected ' + str(kind))\n    if 'enum' in schema and value not in schema['enum']:\n        raise ContractError(path + ': unsupported value')\n    if type(value) in (int, float):\n        if not math.isfinite(value) or value < schema.get('minimum', value) or value > schema.get('maximum', value):\n            raise ContractError(path + ': outside numeric bounds')\n    if isinstance(value, dict):\n        if len(value) > 32: raise ContractError(path + ': too many keys')\n        for name in schema.get('required', []):\n            if name not in value: raise ContractError(path + '.' + name + ': required')\n        props = schema.get('properties', {})\n        for name, item in value.items():\n            if name not in props and schema.get('additionalProperties') is False:\n                raise ContractError(path + '.' + name + ': unknown field')\n            validate_data(item, props.get(name, {}), path + '.' + name)\n    elif isinstance(value, list):\n        if not schema.get('minItems', 0) <= len(value) <= schema.get('maxItems', 4096):\n            raise ContractError(path + ': invalid array length')\n        for i, item in enumerate(value): validate_data(item, schema.get('items', {}), path + '[' + str(i) + ']')\n    elif isinstance(value, str) and len(value) > schema.get('maxLength', 4096):\n        raise ContractError(path + ': string too long')\n\n\ndef verify_source(root, spec):\n    root = Path(root).resolve()\n    for name, expected in spec['source_hashes'].items():\n        target = root / name\n        if target.is_symlink() or not target.resolve().is_relative_to(root) or not target.is_file():\n            raise ContractError('Pinned source missing or outside source root: ' + name)\n        if hashlib.sha256(target.read_bytes()).hexdigest() != expected:\n            raise ContractError('Pinned source checksum changed: ' + name)\n    return root\n\n\ndef load_upstream(root, relative):\n    \"\"\"Only a generator-pinned path is accepted; never a path from task input.\"\"\"\n    target = root / relative\n    for directory in [root, root / 'PathPlanning', target.parent]:\n        sys.path.insert(0, str(directory))\n    module_spec = importlib.util.spec_from_file_location('rsh_upstream', target)\n    module = importlib.util.module_from_spec(module_spec)\n    module_spec.loader.exec_module(module)\n    if hasattr(module, 'show_animation'): module.show_animation = False\n    return module\n\n\ndef clean_json(value):\n    if hasattr(value, 'tolist'): return clean_json(value.tolist())\n    if isinstance(value, dict): return {k: clean_json(v) for k,v in value.items()}\n    if isinstance(value, (tuple, list)): return [clean_json(v) for v in value]\n    if hasattr(value, 'item'): return clean_json(value.item())\n    if isinstance(value, float) and not math.isfinite(value): raise ContractError('Non-finite output')\n    return value\n\n\ndef adapter_main(spec, backend):\n    parser = argparse.ArgumentParser(description=spec['name'])\n    parser.add_argument('--source-dir')\n    group = parser.add_mutually_exclusive_group(required=True)\n    group.add_argument('--input', type=Path, help='UTF-8 JSON file; no code or paths inside task input')\n    group.add_argument('--input-json', help='Inline JSON task input')\n    group.add_argument('--example', action='store_true', help='Run the declared example input')\n    group.add_argument('--describe', action='store_true', help='Read the contract without importing upstream code')\n    args = parser.parse_args()\n    try:\n        if args.describe:\n            print(json.dumps({'skill_id': spec['id'], 'interface': spec['interface'], 'example_input': spec['example']}, allow_nan=False))\n            return 0\n        if args.input:\n            with args.input.open('rb') as stream: raw=stream.read(65537)\n        else: raw=args.input_json.encode() if args.input_json else None\n        if raw is not None and len(raw) > 65536: raise ContractError('Input exceeds 64 KiB')\n        data = json.loads(raw) if raw is not None else spec['example']\n        validate_data(data, spec['interface']['input_schema'])\n        if not args.source_dir: raise ContractError('--source-dir is required for execution')\n        root = verify_source(args.source_dir, spec)\n        os.environ['MPLBACKEND'] = 'Agg'\n        os.environ.setdefault('OMP_NUM_THREADS', '1')\n        random.seed(data.get('seed', 0))\n        # Upstream diagnostic prints do not contaminate the machine response.\n        with contextlib.redirect_stdout(sys.stderr):\n            outputs, checks = backend(root, data, spec)\n        outputs, checks = clean_json(outputs), clean_json(checks)\n        validate_data(outputs, spec['interface']['output_schema'])\n        if not checks or not all(type(v) is bool for v in checks.values()): raise ContractError('Invalid postcondition report')\n        ok = all(checks.values())\n        print(json.dumps({'schema_version':'1','skill_id':spec['id'],'status':'task_complete' if ok else 'task_failed',\n            'success':ok,'outputs':outputs,'checks':checks,'execution_domain':spec['interface']['execution_domain'],\n            'source_commit':spec['source_commit'],'interface_version':'1','hardware_execution':False}, allow_nan=False, separators=(',',':')))\n        return 0 if ok else 1\n    except (ContractError, ValueError, OSError, ImportError) as error:\n        print(json.dumps({'schema_version':'1','skill_id':spec['id'],'status':'failed','success':False,\n            'reason_code':'invalid_input_or_environment','message':str(error),'hardware_execution':False}, allow_nan=False))\n        return 2\n    except Exception as error:\n        print(json.dumps({'schema_version':'1','skill_id':spec['id'],'status':'failed','success':False,\n            'reason_code':'execution_failed','message':str(error)[:1000],'hardware_execution':False}, allow_nan=False))\n        return 1\n\n\n\"\"\"MIT: bounded simulation controllers; no physical I/O or model training.\"\"\"\n\ndef mainstream_backend(root, data, spec):\n    import importlib.metadata\n    import numpy as np\n    for package in spec['packages']:\n        name,version=package.split('==',1)\n        actual=importlib.metadata.version(name).split('+')[0].split('.')\n        expected=version.split('.')\n        while len(actual)>1 and actual[-1]=='0':actual.pop()\n        while len(expected)>1 and expected[-1]=='0':expected.pop()\n        if actual!=expected:raise ContractError('Pinned package version differs: '+name)\n    os.environ.setdefault('MUJOCO_GL','egl')\n    os.environ['HF_HUB_DISABLE_TELEMETRY']='1'\n    os.environ['WANDB_MODE']='disabled'\n    backend=spec['backend'];seed=data['seed']\n    np.random.seed(seed)\n    if backend=='menagerie':\n        import mujoco\n        model=mujoco.MjModel.from_xml_path(str(root/spec['model_path']))\n        state=mujoco.MjData(model)\n        if model.nkey:mujoco.mj_resetDataKeyframe(model,state,0)\n        mujoco.mj_forward(model,state)\n        indices=[]\n        for name in spec['interface']['output_schema']['properties']['joint_positions_rad']['x-order']:\n            joint=model.joint(name).id\n            candidates=[a for a in range(model.nu) if model.actuator_trntype[a]==0 and model.actuator_trnid[a,0]==joint and model.actuator_biasprm[a,1]<0]\n            if len(candidates)!=1:raise ContractError('Expected one position actuator per declared joint')\n            indices.append(candidates[0])\n        joints=[int(model.actuator_trnid[a,0]) for a in indices]\n        qaddrs=[int(model.jnt_qposadr[j]) for j in joints]\n        for a,q in zip(indices,qaddrs):state.ctrl[a]=state.qpos[q]\n        for _ in range(500):mujoco.mj_step(model,state)\n        start=state.qpos[qaddrs].copy();target=start+data['joint_offset_rad']\n        for k,(a,j) in enumerate(zip(indices,joints)):\n            if model.jnt_limited[j]:target[k]=np.clip(target[k],model.jnt_range[j,0]+.02,model.jnt_range[j,1]-.02)\n            if model.actuator_ctrllimited[a]:target[k]=np.clip(target[k],*model.actuator_ctrlrange[a])\n            state.ctrl[a]=target[k]\n        for _ in range(2500):mujoco.mj_step(model,state)\n        final=state.qpos[qaddrs];error=float(np.max(np.abs(final-target)))\n        moved=float(np.max(np.abs(final-start)));demand=float(np.max(np.abs(target-start)))\n        return {'joint_positions_rad':final.tolist(),'target_joint_positions_rad':target.tolist(),'max_error_rad':error,'movement_rad':moved}, {\n            'nontrivial_target':demand>=.03,'joint_goal_reached':error<.035,\n            'actual_motion':moved>=.02,'finite_state':bool(np.isfinite(state.qpos).all() and np.isfinite(state.qvel).all())}\n    if backend=='robosuite':\n        sys.path.insert(0,str(root))\n        import robosuite as suite\n        env=suite.make('Lift',robots=spec['robot'],has_renderer=False,has_offscreen_renderer=False,use_camera_obs=False,\n                       control_freq=20,horizon=500,ignore_done=True,initialization_noise=None)\n        try:\n            obs=env.reset();start=obs['robot0_eef_pos'].copy();initial_cube=obs['cube_pos'].copy()\n            def drive(target,grip,steps):\n                nonlocal obs\n                for _ in range(steps):\n                    action=np.zeros(env.action_dim);action[:3]=np.clip((target-obs['robot0_eef_pos'])*10,-1,1);action[-1]=grip\n                    obs,_,_,_=env.step(action)\n            if spec['operation']=='reach':\n                target=start+np.asarray(data['offset_xyz_m']);drive(target,-1,150)\n                error=float(np.linalg.norm(obs['robot0_eef_pos']-target))\n                return {'final_xyz_m':obs['robot0_eef_pos'].tolist(),'target_xyz_m':target.tolist(),'error_m':error}, {\n                    'nontrivial_target':bool(np.linalg.norm(target-start)>.025),'target_reached':error<.015,\n                    'actual_motion':bool(np.linalg.norm(obs['robot0_eef_pos']-start)>.02),'finite_state':bool(np.isfinite(env.sim.data.qpos).all())}\n            drive(initial_cube+np.array([0,0,.12]),-1,80)\n            drive(initial_cube+np.array([0,0,.005]),-1,100)\n            drive(initial_cube+np.array([0,0,.005]),1,50)\n            drive(initial_cube+np.array([0,0,data['lift_height_m']]),1,120)\n            height=float(obs['cube_pos'][2]-initial_cube[2]);success=bool(env._check_success())\n            return {'cube_lift_m':height,'cube_xyz_m':obs['cube_pos'].tolist(),'environment_success':success}, {\n                'environment_goal':success,'object_lifted':height>.07,'finite_state':bool(np.isfinite(env.sim.data.qpos).all())}\n        finally:env.close()\n    if backend=='gymrobotics':\n        sys.path.insert(0,str(root))\n        import gymnasium as gym,gymnasium_robotics\n        gym.register_envs(gymnasium_robotics)\n        env=gym.make(spec['env_id'],max_episode_steps=4000 if spec['operation']=='maze' else 1000)\n        try:\n            obs,info=env.reset(seed=seed);steps=0\n            def move(target,grip,limit):\n                nonlocal obs,info,steps\n                for _ in range(limit):\n                    action=np.r_[np.clip((target-obs['observation'][:3])*6,-1,1),grip]\n                    obs,_,_,_,info=env.step(action);steps+=1\n            if spec['operation']=='reach':move(obs['desired_goal'],0,100)\n            elif spec['operation']=='pick_place':\n                obj=obs['observation'][3:6].copy();goal=obs['desired_goal'].copy()\n                move(obj+np.array([0,0,.12]),1,35);move(obj+np.array([0,0,.005]),1,30)\n                move(obj+np.array([0,0,.005]),-1,30);move(obj+np.array([0,0,.2]),-1,35)\n                move(goal+np.array([0,0,.08]),-1,40);move(goal,-1,35)\n                move(goal,-1 if goal[2]>obj[2]+.04 else 1,30)\n            elif spec['operation']=='push':\n                obj=obs['observation'][3:6].copy();goal=obs['desired_goal'].copy()\n                direction=goal[:2]-obj[:2];direction/=max(np.linalg.norm(direction),1e-6)\n                behind=obj-np.r_[direction*.08,0]\n                move(behind+np.array([0,0,.12]),1,35);move(behind,1,35)\n                for _ in range(160):\n                    obj=obs['observation'][3:6].copy();delta=goal[:2]-obj[:2]\n                    if np.linalg.norm(delta)<.025:break\n                    aim=goal.copy();aim[2]=obj[2];move(aim,1,1)\n            else:\n                from collections import deque\n                maze=env.unwrapped.maze;grid=maze.maze_map\n                begin=tuple(maze.cell_xy_to_rowcol(obs['achieved_goal']));goalcell=tuple(maze.cell_xy_to_rowcol(obs['desired_goal']))\n                queue=deque([begin]);parents={begin:None}\n                while queue:\n                    cell=queue.popleft()\n                    if cell==goalcell:break\n                    for dx,dy in [(0,1),(0,-1),(1,0),(-1,0)]:\n                        nxt=(cell[0]+dx,cell[1]+dy)\n                        if 0<=nxt[0]<len(grid) and 0<=nxt[1]<len(grid[0]) and grid[nxt[0]][nxt[1]]!=1 and nxt not in parents:\n                            parents[nxt]=cell;queue.append(nxt)\n                if goalcell not in parents:raise ContractError('Goal unreachable in declared maze')\n                route=[];cell=goalcell\n                while cell!=begin:route.append(cell);cell=parents[cell]\n                targets=[maze.cell_rowcol_to_xy(c) for c in reversed(route)]+[obs['desired_goal'].copy()]\n                for target in targets:\n                    for _ in range(160):\n                        error=target-obs['achieved_goal']\n                        if np.linalg.norm(error)<.12:break\n                        velocity=obs['observation'][2:4]\n                        action=np.clip(error*10-velocity*3,-1,1)\n                        obs,_,_,_,info=env.step(action);steps+=1\n                        if steps>=3800:break\n                    if steps>=3800:break\n            error=float(np.linalg.norm(obs['achieved_goal']-obs['desired_goal']))\n            success=bool(info.get('is_success',info.get('success',False)))\n            return {'achieved_goal':obs['achieved_goal'].tolist(),'desired_goal':obs['desired_goal'].tolist(),'error_m':error,'steps':steps,'environment_success':success}, {\n                'environment_goal':success,'finite_state':bool(np.isfinite(obs['observation']).all()),'bounded_steps':steps<=(3800 if spec['operation']=='maze' else 950)}\n        finally:env.close()\n    if backend=='lerobot':\n        sys.path.insert(0,str(root/'src'))\n        os.environ['SDL_VIDEODRIVER']='dummy'\n        import torch,gymnasium as gym,gym_pusht\n        from huggingface_hub import snapshot_download\n        from lerobot.policies.diffusion.modeling_diffusion import DiffusionPolicy\n        torch.manual_seed(seed);torch.cuda.manual_seed_all(seed);torch.set_num_threads(2)\n        if not torch.cuda.is_available():raise ContractError('This validated policy environment requires CUDA')\n        folder=snapshot_download(spec['policy_repo'],revision=spec['policy_revision'],allow_patterns=['config.json','model.safetensors','README.md'])\n        for name,expected in spec['policy_hashes'].items():\n            if hashlib.sha256((Path(folder)/name).read_bytes()).hexdigest()!=expected:raise ContractError('Policy artifact checksum changed')\n        policy=DiffusionPolicy.from_pretrained(folder).to('cuda').eval();policy.reset()\n        env=gym.make('gym_pusht/PushT-v0',obs_type='pixels_agent_pos',render_mode='rgb_array',max_episode_steps=300)\n        try:\n            obs,info=env.reset(seed=seed);maximum=0.;success=False\n            for step in range(300):\n                batch={'observation.image':torch.from_numpy(obs['pixels']).permute(2,0,1).float().div(255).unsqueeze(0).to('cuda'),\n                       'observation.state':torch.from_numpy(obs['agent_pos']).float().unsqueeze(0).to('cuda')}\n                with torch.inference_mode():action=policy.select_action(batch)[0].cpu().numpy()\n                obs,reward,terminated,truncated,info=env.step(action);maximum=max(maximum,float(info['coverage']))\n                success=success or bool(info.get('is_success',False)) or terminated\n                if terminated or truncated:break\n            return {'maximum_coverage':maximum,'steps':step+1,'environment_success':success,'cuda_inference':True}, {\n                'environment_goal':success,'coverage_threshold':maximum>=.95,'cuda_inference':True}\n        finally:env.close()\n    raise ContractError('Unsupported pinned simulation backend')\n\nSPEC = json.loads('{\"id\":\"mujoco-dynamixel-2r-joint-control\",\"name\":\"dynamixel 2r joint-position control\",\"backend\":\"menagerie\",\"operation\":\"joint_control\",\"interface\":{\"version\":\"1\",\"kind\":\"controller\",\"abstraction_level\":1,\"execution_domain\":\"simulation\",\"language\":\"python\",\"input_transport\":\"json_file_or_inline\",\"output_transport\":\"json_stdout\",\"output_selector\":\"outputs\",\"conventions\":{\"linear_unit\":\"m\",\"angular_unit\":\"rad\",\"time_unit\":\"s\",\"coordinate_frame\":\"right_handed_world_xyz; +z_up\",\"joint_order\":\"R2,R1\",\"quaternion_order\":\"not_used\"},\"input_schema\":{\"type\":\"object\",\"required\":[\"seed\",\"joint_offset_rad\"],\"properties\":{\"seed\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":10000},\"joint_offset_rad\":{\"type\":\"number\",\"minimum\":-0.15,\"maximum\":0.15,\"x-unit\":\"rad\"}},\"additionalProperties\":false},\"output_schema\":{\"type\":\"object\",\"required\":[\"joint_positions_rad\",\"target_joint_positions_rad\",\"max_error_rad\",\"movement_rad\"],\"properties\":{\"joint_positions_rad\":{\"type\":\"array\",\"minItems\":2,\"maxItems\":2,\"items\":{\"type\":\"number\",\"minimum\":-20,\"maximum\":20},\"x-unit\":\"rad\",\"x-frame\":\"named_model_joint_coordinates\",\"x-order\":[\"R2\",\"R1\"]},\"target_joint_positions_rad\":{\"type\":\"array\",\"minItems\":2,\"maxItems\":2,\"items\":{\"type\":\"number\",\"minimum\":-20,\"maximum\":20},\"x-unit\":\"rad\",\"x-frame\":\"named_model_joint_coordinates\",\"x-order\":[\"R2\",\"R1\"]},\"max_error_rad\":{\"type\":\"number\",\"minimum\":0,\"maximum\":50,\"x-unit\":\"rad\"},\"movement_rad\":{\"type\":\"number\",\"minimum\":0,\"maximum\":50,\"x-unit\":\"rad\"}},\"additionalProperties\":false},\"preconditions\":[\"Use the exact recorded source, Linux Python 3.10 environment and pinned direct packages.\",\"No physical robot, network actuation or external robot credentials.\"],\"postconditions\":[\"All reported checks must be true and all output numbers finite.\",\"Success covers the declared goal and frozen cases, not arbitrary tasks or physical hardware.\"],\"failure_codes\":[\"invalid_input_or_environment\",\"execution_failed\",\"task_failed\"],\"composition\":{\"depends_on\":[],\"automatic_frame_conversion\":false,\"automatic_hardware_execution\":false}},\"example\":{\"seed\":0,\"joint_offset_rad\":0.08},\"source_hashes\":{\"dynamixel_2r/LICENSE\":\"6dcdd8a1a0e6bc2c26604a3bdb413cb87ca27fd337c0c1065678217ede334aeb\",\"dynamixel_2r/dynamixel_2r.xml\":\"14c7abc024d5855b2c7f6f2d6b24d81e044a5e1f3f11633adeb97a8715037ec3\",\"dynamixel_2r/scene.xml\":\"a320cde82a7aa81f9434b5c85d13d1315ac4c363008309d56af324cb893e3c09\"},\"summary\":\"dynamixel 2r joint-position control. Bounded simulated joint offsets from the original reset pose; not locomotion or object-task validation.\",\"observation_description\":\"Privileged simulator state and declared goals; no perception or real sensor validation.\",\"goal_scope\":\"Move the named actuated hinge joints by a bounded offset from the reset/settled pose; max residual <0.035 rad and nonzero actual motion. This is not end-effector planning, balance, locomotion or hardware control.\",\"model_path\":\"dynamixel_2r/scene.xml\",\"actuator_names\":[\"R2\",\"R1\"],\"source_commit\":\"62ab00f2bc44f6f20c1c9b7a13f3fb1dd41a4e35\",\"packages\":[\"mujoco==3.3.7\",\"numpy==1.26.4\"]}')\n\nif __name__ == \"__main__\":\n    raise SystemExit(adapter_main(SPEC, mainstream_backend))\n",
      "requirements.txt": "mujoco==3.3.7\nnumpy==1.26.4\n",
      "LICENSE": "MIT License\n\nCopyright (c) 2026 Superfast Robotics\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n",
      "LICENSE_SCOPE.md": "# Prepared execution adapter license\n\nThe Hub-authored adapter files in this recipe are MIT licensed; see LICENSE.\nThe source checkout is a separately pinned upstream work. Its original license,\ncopyright and required notices remain applicable. The catalog record's license\ndescribes that upstream source, not a replacement for this adapter's license.\nSeparately licensed dependencies, models, weights, data, firmware and services\nretain their own terms. An adapter's successful simulation or computation does\nnot grant rights to those assets or establish physical robot compatibility.\n"
    }
  },
  "evidence": {
    "level": "task_complete",
    "verified_by": "Hub finite-case Lambda execution harness",
    "date": "2026-10-20",
    "environment": {
      "scope": "simulation",
      "physical_robot": false,
      "platforms": [
        "linux"
      ]
    },
    "commands": [
      "python tools/verify_runtime_expansion.py --backend menagerie --source-cache SOURCE_CACHE --output OUTPUT"
    ],
    "result": "All 3 frozen cases completed against this exact adapter and source.",
    "artifacts": [
      "https://raw.githubusercontent.com/Superfast-Robotics/robot-skill-hub/d800c48cfd84f618a5c6ab84f8f7feec74ab0b56/evidence/runtime/diverse-sim-v1/mujoco-dynamixel-2r-joint-control.json"
    ],
    "runtime_verification": "task_complete",
    "contributor_reported": null,
    "hub_verification": {
      "runtime": "task_complete",
      "scope": "simulation",
      "adapter_sha256": "02e17479ce73a5525e6b5afbb5018657b2eb74ad8412e97460532e98866e7404",
      "source_commit": "62ab00f2bc44f6f20c1c9b7a13f3fb1dd41a4e35",
      "record_version": "1.0.0",
      "case_count": 3,
      "retained_failed_cases": 0
    }
  },
  "maintenance": {
    "record_version": "1.0.0",
    "last_checked": "2026-10-20",
    "upstream_status": "immutable_source_and_explicit_dependencies"
  },
  "contribution": {
    "allowed_paths": [
      "catalog/records/mujoco-dynamixel-2r-joint-control.json",
      "recipes/mujoco-dynamixel-2r-joint-control/run.py",
      "recipes/mujoco-dynamixel-2r-joint-control/requirements.txt",
      "docs/recipes/mujoco-dynamixel-2r-joint-control.md"
    ],
    "instructions": "Change the runtime expansion manifest/template, regenerate, and rerun all frozen cases. Preserve source/model/checkpoint licenses and distinguish simulation from hardware."
  }
}
