This file provides guidance to AI coding agents (Claude Code, OpenAI Codex, etc.) when working with code in this repository.
Isaac Lab-Arena is a composable environment-creation and policy-evaluation library for robotics simulation, built on Isaac Sim 6.0 and Isaac Lab 3.0 Beta. Status: alpha (v0.2.x); APIs are unstable. main is the active development branch.
Recurring multi-step workflows are captured as Agent Skills under skills/, grouped by audience. When a task matches a skill, prefer invoking it over re-deriving the procedure from this file.
The canonical skill sources live under skills/developer/ and skills/user/. These folders describe the primary audience and validation owner; they do not restrict which workflows may reuse a skill. Codex discovers the skills through flat symlinks in .agents/skills/; Claude Code reads the same aliases through the committed .claude/skills symlink. Keep the canonical sources and flat discovery aliases synchronized when adding, removing, or renaming a skill.
Fresh-clone setup (run once):
pre-commit install # on the host — registers git pre-commit hooksCommands that touch Isaac Sim or Arena's package code (tests, training, evaluation, runtime scripts) run inside the local repo clone's Docker container. The repo root is mounted at /workspaces/isaaclab_arena. Inside the container, python is aliased to /isaac-sim/python.sh — prefer the explicit path in docker exec invocations from outside the container, where the alias is not active.
Each clone gets its own container (shared image, per-clone name), so clones run in parallel. Don't hardcode the container name — use the dev-container skill to build, start, attach to, discover, or exec into the local clone's container.
Run as the host user, not root.
docker exec "$ARENA_CONTAINER" su $(id -un) -c \
"cd /workspaces/isaaclab_arena && <command>"Lint and format tooling (pre-commit and the hooks it runs — black, flake8, isort, pyupgrade, codespell) runs on the host.
isaaclab_arena/— core package:tasks/,policy/,evaluation/,embodiments/,scene/,assets/,tests/isaaclab_arena_environments/,isaaclab_arena_examples/,isaaclab_arena_g1/,isaaclab_arena_gr00t/— first-party extension packagesdocker/— container build and run scriptssubmodules/— vendored dependencies (IsaacLab, Isaac-GR00T, …)osmo/— OSMO policy-runner workflowdocs/— Sphinx documentation
- Prefer
assert condition, "message"overif not condition: raise ValueError("message")for internal invariant checks. (Formatting, imports, and typing are enforced bypre-commit— see.pre-commit-config.yaml.) - PR bodies follow
.github/pull_request_template.md— a one-line Summary plus 2–5 detail bullets. Resist the agent default of long, multi-section descriptions. - Attribute docstrings should be included below the attribute, rather than in the class-level docstring.
- Copyright headers: a newly created file uses the current year alone (e.g.
2026); a file created earlier and edited this year uses a range (e.g.2025-2026). Don't copy a neighbouring file's year — the pre-commit hooks (insert-license,fix-new-file-copyright-year) set and enforce this, so you generally don't hand-edit it.
- Prefer one line; a 2–3 line paragraph may follow if needed.
- The docstring should describe the function’s calling syntax and its semantics, but generally not its implementation details, unless those details are relevant to how the function is to be used.
- Document
ArgsandReturns, but notRaises. OmitReturnswhen it only returns None or the summary already covers it. - Don't use Sphinx-style cross-references.
ArenaEnvBuilder.make_registered() returns the gym-wrapped env (not the base env). Use env.unwrapped explicitly to access Isaac Lab-specific attributes (cfg, device, step_dt, etc.) that are not forwarded by gymnasium's OrderEnforcing wrapper:
env = arena_builder.make_registered() # wrapped env
env.step(actions) # goes through OrderEnforcing
env.unwrapped.cfg # access Isaac Lab config
env.unwrapped.device # access Isaac Lab deviceSimulation tests use an inner/outer function pattern to handle Isaac Sim's process lifecycle:
def _test_foo(simulation_app): # runs inside SimulationApp
from isaaclab_arena.X import Y # deferred imports after sim init
...
return True # indicates pass
def test_foo(): # pytest-visible outer function
result = run_function_with_persistent_simulation_app(_test_foo)
assert result- Don't call CLI paths that may invoke
sys.exit—such as argparse--help, invalid arguments, orparser.error()—directly inside pytest. After Isaac Sim starts, catchingSystemExitstill leaves Kit shutdown queued. - Instead, run the CLI with
subprocess.run([TestConstants.python_path, ...])and assert the child result. Usewith_subprocessonly when the child starts Isaac Sim; the marker does not create a child process.
- Never force-push to
mainorrelease/*. Instead, push to a<username>/<type>/<short-description>branch (<type>∈feature,fix,docs,refactor,chore,ci) and open a PR againstmain. - Never add AI-attribution lines to commits (no
Co-Authored-By: Claude…, noGenerated with…). Instead, sign off withgit commit -s— DCO is the only required trailer. - Never commit models, datasets, or secrets. Instead, keep them on the host and mount them via
./docker/run_docker.sh -d <datasets> -m <models> -e <eval>. - Ask first before changing
docker/,.github/workflows/,.pre-commit-config.yaml, orsubmodules/— these affect every contributor. Instead of pushing directly, open a draft PR or raise it in the relevant channel before merging.