|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +This file defines repository-level development rules for people and AI coding agents working in |
| 4 | +the **RoboVerse** content repo (`roboverse-py`). It is read automatically by Claude Code, Codex, |
| 5 | +Cursor, and similar agents at the start of a session. |
| 6 | + |
| 7 | +RoboVerse is the **downstream** half of a two-repo system: |
| 8 | + |
| 9 | +- **MetaSim** (`metasim`, sibling repo) owns the core simulator abstractions, scenario config types, |
| 10 | + task registry, package discovery, and simulator backends. Its rules live in the MetaSim |
| 11 | + [`AGENTS.md`](../MetaSim/AGENTS.md) — follow that file for anything touching core, backends, or the |
| 12 | + `metasim/test` suite. |
| 13 | +- **RoboVerse** (this repo) owns tasks, robots, scenes, grounds, assets, learning code |
| 14 | + (`roboverse_learn`), examples (`get_started`), tooling, and reports. It depends on MetaSim via |
| 15 | + `metasim @ git+https://github.com/RoboVerseOrg/MetaSim.git`. |
| 16 | + |
| 17 | +The goals here are: |
| 18 | + |
| 19 | +- keep RoboVerse easy to extend with new tasks / robots / sims, with a low on-ramp for contributors; |
| 20 | +- protect **multi-simulator parity** — correctness across backends is load-bearing, not a nice-to-have; |
| 21 | +- keep public APIs stable so RoboVerse can become a dependable standard library; |
| 22 | +- prefer local consistency and existing infrastructure over clever new abstractions. |
| 23 | + |
| 24 | +For details that this file points to: |
| 25 | + |
| 26 | +- Contributor setup: [`CONTRIBUTING.md`](./CONTRIBUTING.md) |
| 27 | +- Priorities and version targets: [`ROADMAP.md`](./ROADMAP.md) |
| 28 | +- Tutorials, task migration, API: <https://roboverse.wiki/metasim/> |
| 29 | +- Simulator → environment mapping: MetaSim [`ENVIRONMENTS.md`](../MetaSim/ENVIRONMENTS.md) |
| 30 | + |
| 31 | +--- |
| 32 | + |
| 33 | +## Multi-Repo Workflow |
| 34 | + |
| 35 | +- The two repos are sibling directories. Install both editable |
| 36 | + (`python -m pip install -e ".[dev,mujoco]"` in RoboVerse), with `metasim` resolved from the local |
| 37 | + MetaSim checkout when developing cross-cutting features. |
| 38 | +- **Know which repo owns the change before editing.** A simulator-backend bug, a scenario-config |
| 39 | + type, or the task registry is a *MetaSim* change. A new task/robot/scene, a reward, a learning |
| 40 | + script, or an example is a *RoboVerse* change. |
| 41 | +- For a feature that spans both, land (or at least draft) the **MetaSim change first**, then the |
| 42 | + RoboVerse change that consumes it. Do not work around a missing core capability by duplicating it |
| 43 | + downstream. |
| 44 | +- Do not push core logic into MetaSim that is really RoboVerse content, and do not fork core types |
| 45 | + into RoboVerse. Use MetaSim's package-discovery mechanisms (`metasim.toml`, entry points, |
| 46 | + `METASIM_*_PACKAGES`) to register downstream content. |
| 47 | + |
| 48 | +## General Workflow |
| 49 | + |
| 50 | +- **Orient first.** When unfamiliar with an area, check the docs (<https://roboverse.wiki>, |
| 51 | + `docs/source/`), `ROADMAP.md`, and the relevant `roboverse_pack/` subtree before searching source |
| 52 | + from scratch. |
| 53 | +- **Pre-commit is the gate.** Install once with `pre-commit install`. Before opening a PR, run |
| 54 | + `pre-commit run --all-files` and resolve errors. |
| 55 | +- **Semantic commits.** Use [Conventional Commits](https://www.conventionalcommits.org/): |
| 56 | + `<type>(<scope>): <description>`, types `feat|fix|docs|style|refactor|test|chore`, scope = pack or |
| 57 | + module (e.g. `feat(tasks): add mjlab cartpole balance`, `fix(robots): correct go1 base-vel obs`). |
| 58 | +- Keep commits and pull requests focused, reviewable, and scoped to one concern. |
| 59 | + |
| 60 | +## Parity Is Load-Bearing (RoboVerse-Specific) |
| 61 | + |
| 62 | +RoboVerse's value is that the same task behaves consistently across MuJoCo, Newton, SAPIEN3, |
| 63 | +PyBullet, IsaacSim, etc. Treat parity as a correctness contract: |
| 64 | + |
| 65 | +- **Run the task end-to-end first; chase numerical parity second.** A task that "matches" only |
| 66 | + because both sides are equally broken (e.g. both fall into the void) is not parity. See the parity |
| 67 | + harness pattern in `scripts/parity_obs_reward_cartpole.py` and `scripts/eval_*_cross_sim.py`. |
| 68 | +- When porting a reward / observation / dynamics from another framework (mjlab, ManiSkill, …), aim |
| 69 | + for **bitwise or machine-eps agreement** on obs and reward, and verify with an actual cross-sim |
| 70 | + comparison rather than asserting it. |
| 71 | +- **Closed-loop dynamics parity ≠ obs-bitwise parity.** A policy that trains on one backend may not |
| 72 | + transfer to another even when observations match; report which backend a trained demo actually |
| 73 | + ran on instead of implying transfer. |
| 74 | +- Reports must show pain points honestly. Do not present a clean number that hides a real failure. |
| 75 | + |
| 76 | +## Adding Content (Tasks / Robots / Scenes) |
| 77 | + |
| 78 | +- Follow the task-migration developer guide: <https://roboverse.wiki/metasim/developer_guide/new_task>. |
| 79 | +- Tasks/robots/scenes/grounds live under `roboverse_pack/`. Configs use the `@configclass` dataclass |
| 80 | + pattern from `metasim.utils`; prefer composing existing Cfg types over inventing parallel ones. |
| 81 | +- Prefer extending an existing task family over a new top-level scaffold when the new task is a |
| 82 | + variant. Keep additions additive — do not modify unrelated legacy task files to make a new one work. |
| 83 | +- Register learning entry points and example usage where the existing ones live |
| 84 | + (`roboverse_learn/{rl,il,vla}`, `get_started/`); don't scatter new top-level scripts. |
| 85 | + |
| 86 | +## Design Principles |
| 87 | + |
| 88 | +- Be a library, not a framework. Keep public APIs small, orthogonal, and composable. |
| 89 | +- Duplication is cheaper than the wrong abstraction. Don't add layers the code doesn't yet need. |
| 90 | +- Make it work, make it right, then make it fast — measure before optimizing. |
| 91 | +- Single source of truth: centralize shared constants, configs, and logic; don't fork them per pack. |
| 92 | +- Validate and normalize at boundaries (CLI, config, data loaders). Fail fast on invalid states with |
| 93 | + clear, actionable errors; never fail silently or turn an unsupported path into a quiet no-op. |
| 94 | +- Favor composition over inheritance. Keep side effects explicit and localized. |
| 95 | +- Optimize for readability first; future maintainers and downstream users are users too. |
| 96 | + |
| 97 | +## API Design Guidelines |
| 98 | + |
| 99 | +- **Prefer keyword-only arguments** over long positional lists, so the API can grow without breaking |
| 100 | + call sites: `def attention(*, query, key, value, query_mask, kv_mask): ...`. Obvious operators |
| 101 | + (`matmul(a, b)`) may stay positional. |
| 102 | +- **Return a dataclass / `@configclass`, not a dynamically-sized tuple.** New return fields should not |
| 103 | + break existing callers. A well-known operator signature (e.g. `lstm` → `(output, h, c)`) is the |
| 104 | + exception. |
| 105 | +- For composite configs, hold sub-module `.Config` objects rather than flattening their fields, so |
| 106 | + sub-modules can be swapped without changing the composite surface. |
| 107 | +- Keep public APIs stable. If you must break one, document a migration path. |
| 108 | + |
| 109 | +## Code Style |
| 110 | + |
| 111 | +RoboVerse uses **ruff** (lint + format) and **pre-commit**. The authoritative config is in |
| 112 | +`pyproject.toml` — do not invent rules that contradict it. |
| 113 | + |
| 114 | +### Python |
| 115 | + |
| 116 | +- **Double quotes** (ruff default — match this repo even if a sibling repo uses single quotes). |
| 117 | +- **Line length 120**; `target-version = "py38"`, so keep syntax 3.8-compatible. |
| 118 | +- **Google-style docstrings** (`pydocstyle convention = "google"`). |
| 119 | +- Use `from __future__ import annotations` (the codebase relies on string annotations and `FA`). |
| 120 | +- **Local / lazy imports are allowed here** (`E402`, `PLC0415` are intentionally ignored) because |
| 121 | + heavy optional simulator backends (isaacgym, isaacsim, newton) and the `try: import isaacgym` |
| 122 | + ordering shim must be imported lazily. Prefer top-level imports for stdlib and hard deps, but do |
| 123 | + not "fix" an existing deliberate lazy/ordered import. |
| 124 | +- Don't add global `noqa`/ignore rules to `pyproject.toml`; use per-line `# noqa: CODE` for local |
| 125 | + exceptions. The existing per-file docstring (`"D"`) ignores are tracked debt — when you add a |
| 126 | + docstring, you may remove the matching `FIXME` ignore. |
| 127 | + |
| 128 | +### Commands |
| 129 | + |
| 130 | +- Prefer `python -m <module>` form (e.g. `python -m pip`, `python -m pytest`, `python -m ruff`). |
| 131 | +- Prefer `git -C <dir>` over `cd <dir> && git ...` to avoid changing the working directory. |
| 132 | + |
| 133 | +## Testing |
| 134 | + |
| 135 | +- RoboVerse content/integration tests live in `tests/` (`test_*.py`, functions `test_*`); run with |
| 136 | + `python -m pytest tests/`. Core simulator tests live in MetaSim's `metasim/test` — run those there, |
| 137 | + following MetaSim's `AGENTS.md`. |
| 138 | +- **Be explicit about simulator scope.** Only run a simulator's tests when the change affects that |
| 139 | + simulator or shared code it uses. Read the sim→environment mapping from MetaSim |
| 140 | + [`ENVIRONMENTS.md`](../MetaSim/ENVIRONMENTS.md); if the mapping is unknown, **ask before running |
| 141 | + simulator-backed tests**. |
| 142 | +- **GPU rule.** `isaacgym`, `isaacsim`, and `newton` runs are GPU-backed. If the environment can't |
| 143 | + see a GPU, report it as an environment blocker — don't record it as a normal test result, and |
| 144 | + don't compress setup/GPU failures into "test failures". |
| 145 | +- Add a regression test for every bug fix. Do not claim "all tests pass" unless the exact requested |
| 146 | + commands ran in the correct environments. |
| 147 | + |
| 148 | +## Documentation Workflow |
| 149 | + |
| 150 | +For every non-trivial change (new feature, public behavior, CLI/config/data-format, module |
| 151 | +responsibility, or durable design choice), update docs together with the code: |
| 152 | + |
| 153 | +- User-facing docs are Sphinx under `docs/source/` and publish to <https://roboverse.wiki>. |
| 154 | +- Ground docs in the repo: inspect code before naming paths, functions, classes, configs, or CLI |
| 155 | + flags. If something can't be verified, write `TODO: verify ...` instead of guessing. |
| 156 | + |
| 157 | +## AI-Agent Rules |
| 158 | + |
| 159 | +### Before changing code |
| 160 | + |
| 161 | +- Read the relevant existing code paths and the existing test for the area first. |
| 162 | +- Confirm whether the change belongs in RoboVerse or MetaSim (see Multi-Repo Workflow). |
| 163 | + |
| 164 | +### Before adding files |
| 165 | + |
| 166 | +- Ask: can this be done by cleanly extending an existing file? If yes, do that. New files — and |
| 167 | + especially new top-level scripts or docs — are the exception, not the default. |
| 168 | + |
| 169 | +### Before declaring success |
| 170 | + |
| 171 | +- Say exactly what was verified and how (which command, which sim environment). |
| 172 | +- If a run was blocked by environment/GPU problems, say so and include the real blocker. |
| 173 | +- For parity claims, state the measured delta and the backend(s) actually exercised. |
0 commit comments