> This page is for version 0.6.0.
> For other versions, use one of these documentation indexes:
> - Main (default): https://docs.nvidia.com/nemo/gym/main/llms.txt
> - 0.7.0: https://docs.nvidia.com/nemo/gym/v0.7.0/llms.txt
> - 0.6.0: https://docs.nvidia.com/nemo/gym/v0.6.0/llms.txt
> - 0.5.1: https://docs.nvidia.com/nemo/gym/v0.5.1/llms.txt
> - 0.5.0: https://docs.nvidia.com/nemo/gym/v0.5.0/llms.txt
> - 0.4.0: https://docs.nvidia.com/nemo/gym/v0.4.0/llms.txt
> - 0.3.0: https://docs.nvidia.com/nemo/gym/v0.3.0/llms.txt
> - 0.2.1: https://docs.nvidia.com/nemo/gym/v0.2.1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/nemo/gym/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/nemo/gym/_mcp/server.

# Development Setup

This guide covers everything you need to set up your development environment and submit contributions to NeMo Gym.

## Quick Start

```bash
# Clone and set up development environment
git clone git@github.com:NVIDIA-NeMo/Gym.git
cd Gym
curl -LsSf https://astral.sh/uv/install.sh | sh
source $HOME/.local/bin/env
uv venv --python 3.13.14
source .venv/bin/activate
uv sync --extra dev

# Install pre-commit hooks (required for contributors)
pre-commit install
```

## Development Commands

**Run NeMo Gym Tests**:

```bash
gym dev test                                   # Run all NeMo Gym core tests
gym env test --resources-server example_single_tool_call    # Test a single resources server
gym env test --all                             # Run all server tests (slow: one venv per server)
```

**View Test Coverage**:

```bash
coverage html                                  # Generate HTML coverage report
```

**Configuration Debugging**:

```bash
gym env resolve                                # Dump config as NeMo Gym sees it
```

---

## CI/CD Requirements

All contributions must pass these automated checks:

**Required Checks**:

* **Unit Tests**: All existing tests must pass
* **Build Docs**: Documentation must build without errors
* **Copyright Check**: All files must have proper copyright headers
* **DCO Sign-off**: All commits must include `Signed-off-by` (`git commit -s`)
* **Pre-commit Hooks**: Code formatting and linting

**Test Requirements**:

* At least one test per server you contribute
* Tests must run using `gym env test --resources-server your_server`
* Use pytest for async testing patterns

### Build Docs CI Failures

If the build-docs check fails:

1. **Test documentation locally**: Follow the [Fern docs quickstart](https://github.com/NVIDIA-NeMo/Gym/blob/main/fern/README.md).
   * For local documentation previews, ensure Node.js 22+ is installed.
   * From the repository root, run `make docs-login` once to provision and authenticate Fern.
   * From the repository root, run `make docs` to generate the API reference and start the local preview.
   * After the initial generation, prose-only changes can use `cd fern && npm run dev`.

2. **Common issues**:
   * Missing docstrings in public functions
   * Broken markdown links in README or tutorials
   * Invalid MDX syntax

### Contributing documentation (including external contributors)

You do **not** need Fern dashboard org membership to contribute docs.

1. Edit MDX under `fern/versions/latest/pages/`.
2. If you add a page that is not auto-discovered, update the bleeding-edge nav in [`fern/versions/main.yml`](https://github.com/NVIDIA-NeMo/Gym/blob/main/fern/versions/main.yml). Folders mounted with `title-source: frontmatter` usually pick up new pages automatically; otherwise add a `- page:` entry under the right section. See the [Fern docs README](https://github.com/NVIDIA-NeMo/Gym/blob/main/fern/README.md) and the `nemo-gym-docs` skill.
3. Open a pull request from a fork or branch.
4. Rely on CI: `fern check` and the automatic Fern preview comment use the repository `DOCS_FERN_TOKEN`, including for fork PRs.

Optional local preview follows the Fern README. Full `make docs` / library autodoc (`fern docs md generate`) may require membership in the Fern `nvidia` organization if you hit `HTTP 403: User does not belong to organization`.

**Requesting Fern `nvidia` org access (optional):** open a [GitHub issue](https://github.com/NVIDIA-NeMo/Gym/issues/new/choose) on NVIDIA-NeMo/Gym. Suggested title: `Fern nvidia org access request`. Add the `community-request` label if you can, and say you need Fern dashboard membership for local autodoc (`make docs` / `fern docs md generate`). Maintainers triage the request.

**NVIDIA employees:** if you already have NVIDIA Slack, you can also ask in `#fern` for org access or docs help. Slack `#fern` is not required for external contributors.

### Copyright Header Errors

**Error**: "Found files with missing copyright"

**Solution**: Add this header to all new Python files:

```python
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
```

---

## DCO and Commit Signing

All NeMo Gym contributions require **DCO sign-off** (`git commit -s`). Cryptographic commit signing (SSH/GPG `-S`) is **optional** and not enforced by CI.

### Quick Setup (required)

```bash
# Set your identity (use your GitHub email)
git config --global user.name "Your Name"
git config --global user.email "your@github-email.com"
```

**Every commit must be signed off** with `-s`:

```bash
git add .
git commit -s -m "Your commit message"

# The -s flag adds this line automatically:
# Signed-off-by: Your Name <your@email.com>
```

In VS Code / Cursor, you can set `"git.alwaysSignOff": true` so the IDE adds the trailer for you.

### Troubleshooting DCO

**Problem**: "DCO sign-off missing"

**If you have not pushed yet:**

```bash
# Fix the last commit
git commit --amend -s --no-edit

# Fix multiple local commits
git rebase --signoff HEAD~3  # Adjust number as needed
```

**If you already pushed to your PR branch:**

Force-pushing is disallowed on branches in the upstream `NVIDIA-NeMo/Gym` repo. If your PR branch is in a fork and your fork allows force pushes, you can re-sign commits locally and push with `--force-with-lease`; otherwise, push the signed history to a new branch and update the PR.

```bash
# Option 1: Re-sign commits locally, then push to a new branch and update your PR
git rebase --signoff origin/main
git push origin HEAD:your-user/your-feature-dco-fix

# Option 2: Squash into one signed commit on a fresh branch
git checkout -b your-feature-dco-fix origin/main
git merge --squash your-feature-branch
git commit -s -m "Your commit message"
git push -u origin your-feature-dco-fix

# Option 3 (forks only): Re-sign and force-push to the same branch
git rebase --signoff origin/main
git push --force-with-lease
```

**Tip:** Always use `git commit -s` when creating commits to avoid this issue.

### Optional: Cryptographic commit signing (SSH or GPG)

Not required for merge. If you want GitHub to show commits as "Verified":

```bash
# SSH signing (recommended)
git config --global commit.gpgsign true
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
```

Add the same public key in [GitHub Settings → SSH and GPG keys](https://github.com/settings/keys) as a **Signing Key**, then commit with `-s` as usual (add `-S` only if you want to force a signed commit from the CLI).

For GPG instead of SSH: generate a key, set `user.signingkey` to the key ID, leave `gpg.format` unset, and upload the public key under GitHub GPG keys.

## Common Issues

### Testing Issues

**Problem**: Tests fail locally but not in CI

* Check Python version (3.13.14+ required)
* Ensure all dependencies installed: `uv sync --extra dev`
* Run in clean environment

**Problem**: Async test failures

* Use `pytest-asyncio` for async tests
* Mark async tests with `@pytest.mark.asyncio`
* Ensure proper fixture cleanup

### Pre-commit Hook Failures

**Problem**: Pre-commit hooks fail

```bash
# Fix common issues
pre-commit run --all-files        # Run all hooks manually
pre-commit autoupdate             # Update hook versions
```

**Common fixes**:

* `ruff check --fix .` for linting
* `ruff format .` for formatting
* Add copyright headers to new files

---

## Use of AI and LLM Tools

We encourage contributors to use AI coding assistants (Cursor, Claude, Codex, OpenCode, and similar) where they genuinely help. However, AI assistance does not replace human understanding, judgment, and accountability.

**Guiding principle:** Prefer contributions where your review and ownership clearly outweigh the maintainer review burden. You are responsible for every line of code you submit, regardless of whether you or an AI tool wrote it.

The quality bar for PRs (focused diffs, real rollouts for env/agent work, CI-aligned local checks, substantive tests) lives in [`AGENTS.md`](https://github.com/NVIDIA-NeMo/Gym/blob/main/AGENTS.md) so coding agents and humans share one source of truth. See also [Agent Skills](/contribute/agent-skills).

### When We Request Changes

When a pull request or issue appears to be a low-effort, AI-generated submission, maintainers will request changes and explain what needs to improve. If the feedback goes unaddressed — or the contribution is clearly unreviewed bulk output with no path to a quality bar — it may be closed. Common indicators that prompt a change request include:

* Boilerplate or generic code that ignores project conventions
* PRs that do not pass CI or pre-commit checks
* Descriptions or comments that are clearly unreviewed LLM output
* Bulk "improvements" with no corresponding issue or discussion

This is not about policing tool usage. It is about maintaining the quality bar that the community and maintainers depend on.

---

## Development Workflow

For a small, self-contained change, open a single PR:

1. **Create a feature branch**: `git checkout -b <username>/<feature>`
2. **Make changes** with tests
3. **Run local checks**: `gym dev test && pre-commit run --all-files`
4. **Commit with signoff**: `git commit -s -m "Your message"`
5. **Push and open a PR**: ensure all CI checks pass
6. **Address review feedback** and iterate

### Stacked Pull Requests

For larger changes, NeMo Gym uses [GitHub Stacked PRs](https://github.github.com/gh-stack/): a chain of branches where each PR targets the branch below it, so each layer is reviewed as its own focused diff. See GitHub's [Stacked PRs overview](https://github.github.com/gh-stack/) for the concept and PR UI; the mechanics of the `gh stack` CLI in this repo follow.

#### Prerequisites

Install the [GitHub CLI](https://cli.github.com/) (`gh`) v2.0 or later, the `gh stack` extension, and the agent skill:

```bash
gh extension install github/gh-stack
gh skill install github/gh-stack gh-stack   # vendored under .agents/skills/gh-stack/; this installs it for your agent
```

> **Warning**
>
> `gh stack` requires OAuth — Personal Access Tokens are rejected during the private preview. If `gh auth status` shows a `github_pat_…` token, `submit`/`push`/`sync` fail with exit code 9. Re-authenticate with the web flow:
>
> ```bash
> gh auth login   # GitHub.com → HTTPS → "Login with a web browser"
> ```

A stack is created in the repo you push to, and that repo must have the preview enabled — push your branches to the remote that points at `NVIDIA-NeMo/Gym`. With multiple remotes, set the target once (or pass `--remote <name>` on each command) and enable conflict memory so commands do not prompt:

```bash
git config remote.pushDefault upstream   # the remote pointing at NVIDIA-NeMo/Gym
git config rerere.enabled true
```

#### Create a Stack

Build the stack bottom-up, one logical change per branch, where each branch depends on the one below it. Keep a contribution self-contained — land code together with its tests in a single PR — and stack genuinely dependent follow-ups (for example, documentation) on top.

```bash
# Bottom layer: the contribution (resources server, agent config, and its tests) as one branch.
gh stack init -p <user>/my-env contribution
git add resources_servers/my_env/
git commit -s -m "feat: add my_env environment and tests"

# Next layer: a follow-up that depends on the contribution above.
gh stack add docs
git add fern/versions/latest/pages/environment-tutorials/my-env.mdx
git commit -s -m "docs: document my_env"

# Push every branch and open a draft PR per layer (--auto titles them).
gh stack submit --auto

# Inspect the result (always --json; bare `view` opens a TUI).
gh stack view --json
```

`submit` sets each PR's base to the branch below it and links them as a stack on GitHub. Run every `gh stack` command non-interactively: pass branch names to `init`/`add`/`checkout`, `--auto` to `submit`, and `--json` to `view`. Without these flags the commands prompt or open a TUI and hang.

#### Update a Stack

Make a change on the branch it belongs to, then rebase the layers above it:

```bash
gh stack down              # or: gh stack checkout <branch|pr-number>
git add <files> && git commit -s -m "..."
gh stack rebase --upstack  # replay the layers above onto the change
gh stack push
```

After a PR merges, sync to fast-forward the trunk and rebase the rest of the stack:

```bash
gh stack sync --prune      # fetch, rebase, push, drop merged branches
```

On a rebase conflict (exit code 3), resolve the listed files, `git add` them, then `gh stack rebase --continue` (or `--abort`). Merging is done from the GitHub PR UI; CLI merge is not supported yet.

#### Command Reference

The vendored skill (`.agents/skills/gh-stack/SKILL.md`) carries the full reference, exit codes, and non-interactive rules for agents.

| Task                            | Command                                   |
| ------------------------------- | ----------------------------------------- |
| Start a stack                   | `gh stack init -p <prefix> <branch>`      |
| Add a layer                     | `gh stack add <suffix>`                   |
| Push and open PRs               | `gh stack submit --auto`                  |
| Inspect state                   | `gh stack view --json`                    |
| Navigate                        | `gh stack up` / `down` / `top` / `bottom` |
| Rebase after a lower-layer edit | `gh stack rebase --upstack`               |
| Sync after merges               | `gh stack sync --prune`                   |

See the [gh-stack documentation](https://github.github.com/gh-stack/) for the full feature set.

---

**Questions?** Check existing issues or create a new one for guidance.