Warp is an agentic development environment, born out of the terminal.

RAW Rules

AGENTS.md

# Warp Development Guidelines

- Prefer `uv run` to run Python (`uv run script.py`, `uv run -m module_name`, `uv run python -c "..."`), or activate `.venv` first. Do not use the system Python directly.
  - With extras: `uv run --extra dev -m warp.tests`
  - With additional packages: `uv run --with rich script.py`
- If `warp/bin/` is empty, build first with `build_lib.py` (or `--quick`).
- Never use `python -c "..."` to run temporary scripts that define `@wp.kernel` functions—always write to a `.py` file because Warp's codegen calls `inspect.getsourcelines()`, which fails for code not in a file.
- Always capitalize proper names in docstrings and error messages (NumPy, not numpy; Warp, not warp).
- Use subscript syntax for Warp array types in annotations and overload signatures, such as `wp.array2d[float]`. Do not use factory syntax, such as `wp.array2d(dtype=float)`, unless a test specifically covers factory-style compatibility.
- Create a feature branch before committing—never commit directly to `main`. Use `username/short-description`.
- Always use imperative mood in commit messages ("Fix X", not "Fixed X"), ~50 char subject, reference issues as `(GH-XXX)`. Body explains *why*, not what.
- Always use `git commit --signoff` (or `-s`) to add a `Signed-off-by` line (DCO).
- After rebasing, diff `CHANGELOG.md` against the target branch to catch duplicate entries or other issues from clean but incorrect merges.
- CI lives in both GitLab (`.gitlab-ci.yml`) and GitHub (`.github/workflows/`). Lightweight jobs (linting, docs, packaging) may exist in both—keep them in sync. GPU-dependent jobs differ by platform.
- Pin GitHub Actions to commit hashes, not tags, and add a comment with the corresponding tag. Good: `uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v4`. Bad: `uses: astral-sh/setup-uv@v4`. The internal `nv-gha-runners/setup-proxy-cache` action is exempt and may use `@main`.
- Let uv infer Python version from `.python-version`. Use `uv python install` without arguments.

Run `uvx pre-commit run --files <files>` to format changed files, or `-a` for all files. Rebuild native libraries only after changes to `warp/native/` C++/CUDA code:

- Standard build: `uv run build_lib.py` (~5 min)
- Quick build: `uv run build_lib.py --quick` (~2-4 min)
  - Only use if CUDA driver version ≥ Toolkit version
  - Check: `nvidia-smi` (driver) vs Toolkit (set via WARP_CUDA_PATH, CUDA_HOME, CUDA_PATH, or `which nvcc`)

Build docs (~1 min) with `uv run --extra docs build_docs.py 2>&1 | tee /tmp/build_docs.log` so you can inspect warnings without re-running.

## PR Instructions

- If opening a pull request on GitHub, use the template in `.github/PULL_REQUEST_TEMPLATE.md`.
- If opening a merge request on GitLab, use the template in `.gitlab/merge_request_templates/Default.md`. If a GitHub issue exists for the change, end the MR title with a reference (e.g., `[GH-123]`).
- Follow `changelog/README.md`: add a fragment when a change affects users,
  and do not edit `CHANGELOG.md` directly.
- For complex features, consider adding a design doc in `design/`. See `design/README.md` for guidelines.

## Tests

- Keep `warp/tests/**` for core Warp Python library unit tests. Do not add
  meta-tests there for docs config, CI/release scripts, packaging, or test
  harness machinery; verify those workflows directly instead.

Always use `unittest`, not pytest.

- Run all tests (~10-20 min): `uv run --extra dev -m warp.tests -s autodetect`
- Run all tests in a specific file: `uv run warp/tests/test_modules_lite.py`
- Prefer running multiple test classes in parallel over running test files one at a time in a loop: `uv run --extra dev -m warp.tests -s autodetect -k TestArray -k TestCodeGen -k TestFunc`
- Always add new test modules to `default_suite` in `warp/tests/unittest_suites.py`.
- Use standard `unittest.TestCase` methods when tests target a fixed device (e.g., CPU-only). Use `add_function_test()` only when tests need to run across multiple devices via `get_test_devices()`.
- Avoid timing-based assertions (e.g., asserting speedup ratios)—they are flaky in parallel CI environments with variable CPU load.
- Never call `wp.clear_kernel_cache()` or `wp.clear_lto_cache()` in test files—not in `__main__` blocks, test methods, or module scope. Cache clearing is not multi-process-safe; concurrent clears cause LLVM crashes. The test suite runner and `build_lib.py` already handle this.
- Use `np.testing.assert_allclose()` instead of `np.allclose()` for detailed error messages on failure.
- Explicit synchronization is rarely needed for correct behavior—most operations (e.g., `.numpy()`) implicitly synchronize. The exception is unit tests where a test function doesn't call an implicit sync before returning; add `wp.synchronize_device()` there due to the async nature of the CUDA API. Prefer `wp.synchronize_device()` over `wp.synchronize()` (the latter syncs all devices).

## Docstrings

Follow Google-style docstrings. Use doctest for code examples where practical.

- Document `__init__` parameters in the class docstring, not the `__init__` method
- Don't repeat default values from signatures—Sphinx autodoc shows them automatically
- Use double backticks for code elements and parameter cross-references (RST syntax): ``` ``data`` ```, ``` ``.nvdb`` ```—not italics (`*data*`)
- Use attribute docstrings (`"""..."""` after members) for enum/class constant docs, not `#:` comments—Sphinx supports both but only attribute docstrings work in VSCode/Pylance
- Use Sphinx roles for cross-references: `:class:`warp.array``, `:func:`warp.launch``, `:mod:`warp.render``, `:attr:`FILTER_LINEAR``
- In `builtins.py`, use `Args:` and `Returns:` (Google style), not `:param:` and `:returns:` (RST style)

## Codebase Internals

- The native library (`build_lib.py`) statically embeds: NVRTC (CUDA runtime compiler, Linux/Windows only), LLVM/Clang (CPU kernel compiler), and libmathdx (cuBLASDx/cuFFTDx for tile operations, Linux/Windows only).
- JIT compilation artifacts (`.cpp`/`.cu` from `codegen.py`, `.cubin`/`.ptx`/`.o`) live in a kernel cache directory (see `warp/_src/build.py:init_kernel_cache`).
- Refer to `warp/examples/` for reference patterns for kernels, launches, and array usage.
- Always import from `warp` in public-facing code, not `warp._src`. In internal code, import directly from `warp/_src/` modules. Public API is re-exported through `warp/__init__.py`.
- `warp._src.utils` imports `warp._src.context` at module level—importing from `utils` in early-loaded modules (e.g., `texture.py`) causes circular imports. Use lazy imports (`from warp._src.utils import ... # noqa: PLC0415` inside functions) when needed.
- Use the `DeviceLike` type annotation (from `warp._src.context`) for `device` parameters. In modules where importing it at runtime would create a circular import, import it under `TYPE_CHECKING`.
- Native bindings use ctypes; function signatures are registered in `Runtime.__init__` in `warp/_src/context.py`.