Skip to content

sim/mujoco: replace string-concat MJCFBuilder with mujoco.MjSpec AST [umbrella] #121

@cagataycali

Description

@cagataycali

Umbrella issue tracking the MJCF AST refactor documented in IDEA.md at repo root.

TL;DR

mujoco.MjSpec is the official editable MJCF AST shipped with MuJoCo 3.2+. We currently build MJCF by string-concatenating f-strings (mjcf_builder.py) and mutate scenes by round-tripping XML through xml.etree.ElementTree (~600 lines of helpers in scene_ops.py). Switching to MjSpec deletes most of that, kills several bug classes (camera orientation, keyframe-dim mismatch, mesh-path patching), and unlocks two new capabilities:

  1. Agent-authored raw MJCF - validated by actually compiling it, with a clean fallback path.
  2. Fine-grained live mutation - add/remove/modify bodies, geoms, sensors, tendons, equality constraints without a tmpdir + regex round-trip.

Staged plan (one PR per stage)

  • Stage 0 + 1 + 2 - MjSpec builder alongside legacy, feature-flagged. Done in commit ad1d298 on feat/mjspec-refactor.
  • Stage 3 - single-robot attach via spec.attach(robot_spec, prefix=..., frame=...). Replaces parts of scene_ops.inject_robot_into_scene.
  • Stage 4 - multi-robot compose via repeated attach(). Deletes _prefix_robot_names, _namespace_robot_default_classes, compose_multi_robot_scene.
  • Stage 5 - port scene_ops.inject_* / eject_* to spec.recompile(model, data). Handles keyframe-dim mismatch natively.
  • Stage 6 - expose replace_scene_mjcf(xml) and patch_scene_mjcf(ops) as AgentTool actions. New capability: agent-authored raw MJCF including elements SimObject can't express (<tendon>, <equality>, <pair>, custom friction/solref, etc.).
  • Stage 7 - remove feature flag, delete mjcf_builder.py, audit scene_ops.py (should shrink from ~980 to <500 lines).

Success criteria

  • mjcf_builder.py deleted.
  • scene_ops.py under 500 lines.
  • All existing tests pass.
  • One integration test proves an agent can author raw MJCF with a <tendon> or <equality> element (unexpressible via SimObject).
  • No test asserts on exact XML strings.
  • grep -r "f'<" strands_robots/simulation/mujoco/ returns nothing.

Risks (from IDEA.md)

  1. recompile(model, data) preserves qpos only when joint dims unchanged. Adding a freejoint changes nqpos. Mitigated per-stage as we port the inject path.
  2. spec.to_xml() is canonical, not byte-identical to input. Any test asserting exact XML strings is wrong and should be rewritten against compiled model properties.
  3. attach() default-class naming differs from current _namespace_robot_default_classes. Concrete test needed with 2 robots from different URDFs.
  4. MuJoCo compiler errors are C-level and sometimes cryptic. Wrap spec.compile() with context about which op was being applied.

Coordination

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions