Skip to content

[WARP] Cleanup Part 1: Add frontend bridge for stable manager-based tasks - #5504

Merged
kellyguo11 merged 8 commits into
isaac-sim:developfrom
hujc7:jichuanh/warp-manager-bridge
Jul 24, 2026
Merged

kellyguo11 merged 8 commits into
isaac-sim:developfrom
hujc7:jichuanh/warp-manager-bridge

Conversation

@hujc7

@hujc7 hujc7 commented May 5, 2026 •

Copy link
Copy Markdown
Collaborator

Review Map

PR Status Depends on Exact changes
📌 #5504 Part 1: frontend bridge (stable ids on the warp runtime) — —
#6607 Part 2: mask-first frontend (draft) #5504 link
#6646 Part 3: scene reset mask-native + capture-ready (draft, base rewritten — pending rebase onto #6607) #6607 link
#6580 Part 4: OVPhysX backend (draft) #5504 link
#6611 Part 5: persistent-launch speedups (draft, absorbs #6551) #6646 link

1. Summary

  • --frontend {torch,warp} on the shared RL CLI (train and play) runs stable task ids on the warp runtime — no duplicated configs, no parallel warp task registrations at all (end state pinned by test). The rlinf integration constructs environments inside the external framework and is not frontend-routable (known limitation).
  • 1.24–1.70× faster end-to-end than torch on the same tasks, identical Newton physics (§3.1).
  • Manager-based: WarpFrontend adapts the stable cfg in place; MDP twins resolve by name on the mirrored experimental package tree — deterministic, registration-free, missing twins reported in one aggregated failure.
  • Direct: the stable registration declares its warp class via warp_entry_point; only the env class swaps, the cfg is shared.
  • Stable observation-noise cfgs (constant/uniform/gaussian) swap to warp-native twins during adaptation; cfgs without a twin (class-based noise models, customized funcs, tensor-valued params) are a hard error instead of silently dropping noise. The warp observation manager rejects non-warp noise cfgs and plumbs the shared per-env RNG state.
  • Plus: warp adapters for the material/mass randomization startup events; isaaclab_tasks_experimental mirrors the stable core/<task> layout; Metrics/success_rate restored under warp.

2. Migration map

Retired Replacement
Isaac-{Cartpole,Humanoid,Ant}-Warp-v0, Isaac-Reach-Franka-Warp-v0 (+-Play) stable id + --frontend warp presets=newton_mjwarp
Isaac-Velocity-Flat-<Robot>-Warp-v0 (+-Play), all robots stable id + --frontend warp presets=newton_mjwarp
Isaac-{Cartpole,Ant,Humanoid}-Direct-Warp-v0, Isaac-Reorient-Cube-Allegro-Direct-Warp-v0 stable id + --frontend warp presets=newton_mjwarp (via warp_entry_point)

3. Test plan

  • Merged current develop; ./isaaclab.sh -f clean on all files.
  • Re-merged develop (task-id catalog update Update environment catalog and task IDs #6606, entrypoints refactor); environments catalog regenerated; conversion sweep + warp smokes (Isaac-Velocity-Flat-UnitreeGo2, Isaac-Cartpole-Direct) re-run green; pre-commit clean on all files.
  • 142 focused tests: frontend units, cfg-conversion sweep over every covered stable id, no-warp-registrations end-state pin, shared-CLI tests.
  • 5-iteration RSL-RL trainings exit clean (Training time: present, no traceback): Isaac-Cartpole (torch); Isaac-Cartpole, Isaac-Humanoid, Isaac-Cartpole-Direct, Isaac-Ant-Direct, Isaac-Velocity-Flat-AnymalD, Isaac-Velocity-Flat-H1 (warp; randomization events active); Isaac-Reorient-Cube-Allegro-Direct (warp). Metrics/success_rate reported under warp.

3.1 Frontend A/B benchmark

Same stable task id and cfg on both sides, presets=newton_mjwarp on both (identical Newton physics — only --frontend differs); rsl_rl, 4096 envs, 300 iterations, headless, one L40 per run, single run per cell. Metric: rsl_rl Training time: — learn-loop wall-clock (env stepping + PPO update; warp includes its one-time CUDA-graph capture). PPO-update cost is common to both, so the env-pipeline gain exceeds the end-to-end ratio.

Task torch warp end-to-end final reward (torch / warp)
Isaac-Cartpole 58.2 s 39.7 s 1.46× 4.95 / 4.95
Isaac-Humanoid 162.0 s 110.5 s 1.47× 149.1 / 179.8
Isaac-Ant 109.8 s 64.5 s 1.70× 82.1 / 77.1
Isaac-Velocity-Flat-AnymalD 162.4 s 130.7 s 1.24× 20.3 / 21.4

4. Out of scope / follow-ups

  • No video-recorder plumbing on warp envs; rough terrain blocked on Warp TerrainImporter APIs; no twins for IK/OSC actions, height_scan, or Digit/Spot-specific terms.
  • OVPhysX support for the warp frontend prepared as a follow-up PR (physics gate widening + backend data-parity test).
  • Candidate refactor: fold the ~300 lines of converged plumbing shared by DirectRLEnvWarp and stable DirectRLEnv into a common base.

@hujc7
hujc7 requested a review from ooctipus as a code owner May 5, 2026 16:42
@github-actions github-actions Bot added the isaac-lab Related to Isaac Lab team label May 5, 2026

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Isaac Lab Review Bot

Summary

This PR introduces a WarpFrontend adapter that allows stable manager-based RL task configs to run on the experimental warp runtime without requiring parallel -Warp-v0 task registrations. The implementation mutates configs in-place to swap stable MDP functions with warp twins, upgrades SceneEntityCfg instances, and drops unsupported sensors. The approach is architecturally sound for reducing task duplication, but there are several correctness issues that need attention before shipping.

Architecture Impact

The changes are relatively self-contained within isaaclab_experimental, with the main integration point being rsl_rl/train.py. The adapter operates at config mutation time before env construction, so it doesn't change the warp runtime itself. However:

  • The manager_term_cfg.py change from custom classes to re-exports affects any code doing isinstance checks against the warp term cfg types
  • The --manager=warp flag is only added to rsl_rl/train.py, not other trainers (skrl, rl_games) — documented as out of scope but creates API inconsistency
  • The in-place __class__ mutation pattern in _upgrade_scene_entity_cfgs is fragile if the warp SceneEntityCfg ever diverges structurally from stable

Implementation Verdict

Minor fixes needed — The core design is solid, but there are edge cases and correctness issues that should be addressed.

Test Coverage

Insufficient. The PR adds no automated tests. The validation is entirely manual (described in PR description). Given the complexity of the adapter logic (module resolution, class swapping, term iteration), this needs at minimum:

  • Unit tests for WarpFrontend.adapt() with mock configs
  • Regression test for the visualizer fix in manager_based_env_warp.py
  • Integration test that the --manager=warp flag correctly invokes the adapter

CI Status

No CI checks available yet — cannot assess whether existing tests pass.

Findings

🔴 Critical: warp_frontend.py:278-287 — In-place __class__ mutation is unsafe without verifying inheritance

The code does obj.__class__ = _WarpSE after checking isinstance(obj, _StableSE), but if _WarpSE doesn't actually inherit from _StableSE (which the code assumes but doesn't verify), this will corrupt the object. The comment claims "warp inherits stable" but there's no runtime assertion. If someone refactors the warp SceneEntityCfg to not inherit from stable, this silently corrupts configs.

# Should verify at import time:
if not issubclass(_WarpSE, _StableSE):
    raise TypeError("WarpFrontend requires warp SceneEntityCfg to inherit from stable")

🔴 Critical: warp_frontend.py:95-97 — Iterating over dir(group) while mutating attributes

The apply_to method iterates over dir(group) and calls setattr(group, name, None) to drop terms. While this doesn't crash because the iteration is over a snapshot from dir(), it means subsequent code that relies on attribute presence (like the warp manager's term resolution) will see None values rather than missing attributes. The warp managers need to handle None term entries, or this should use delattr.

🟡 Warning: warp_frontend.py:316-329 — Module resolution swallows all exceptions

try:
    import gymnasium as gym
    entry = gym.spec(task_id).kwargs.get("env_cfg_entry_point")
except Exception:
    entry = None

This catches all exceptions including KeyboardInterrupt and SystemExit. Should be except (ImportError, KeyError, AttributeError): or similar to avoid masking real errors.

🟡 Warning: train.py:100-101 — Preset injection doesn't check for conflicting explicit presets

if args_cli.manager == "warp" and not any(a.startswith("presets=") for a in remaining_args):
    remaining_args.append("presets=newton")

If user passes presets=physx, this silently does nothing (doesn't append newton), but the warp adapter may still try to run and fail confusingly later. Should warn when --manager=warp is used with a non-newton preset.

🟡 Warning: warp_frontend.py:215-217 — Physics preset extraction logic is redundant

The comment says "by the time we run, Hydra's resolve_presets has already collapsed any PresetCfg wrapper" — but then the code checks hasattr(physics, "newton") anyway. If the preset is already resolved, this condition will always be False. This is dead code that adds confusion.

🔵 Improvement: warp_frontend.py:170-177 — DEFAULT_TERM_GROUPS misses commands group

The default term groups don't include ("commands",), which is a manager group in the stable env. If a task has command terms with SceneEntityCfg params, they won't be upgraded. Either add it or document why it's excluded.

🔵 Improvement: manager_term_cfg.py — Re-export change may break type annotations

The change from explicit @configclass definitions to from ... import * means type checkers can no longer see what's exported from this module. Code doing from isaaclab_experimental.managers.manager_term_cfg import RewardTermCfg will work at runtime but may fail type checking. Consider adding explicit __all__ or type stub.

🔵 Improvement: warp_frontend.py:256-261 — build() ignores the adapt report

The build() method calls adapt() but doesn't return or expose the report. If the caller wants to inspect what was dropped/missing, they have to call adapt() separately first. Consider returning a tuple (env, report) or attaching the report to the env.

@greptile-apps

greptile-apps Bot commented May 5, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces a --frontend {torch,warp} CLI flag for rsl_rl/train.py that routes any stable manager-based task through a new WarpFrontend adapter instead of requiring a parallel *-Warp-v0 task registration. It also syncs three warp env classes to the current SimulationContext API and deletes the now-redundant experimental manager_term_cfg.py shim.

Confidence Score: 4/5

Safe to merge with the two robustness fixes applied; the warp adapter path is gated behind an optional import so the default torch path is unaffected.

The _walk_terms function iterates dir(node) rather than __dataclass_fields__, which can cause the walker to visit bound methods and property-sourced configclass objects outside the declared cfg tree. The _swap_mdp function accesses stable.__name__ unconditionally and will raise AttributeError for any callable lacking __name__. These are robustness gaps in the hot adapter path.

source/isaaclab_experimental/isaaclab_experimental/envs/frontend.py — _walk_terms and _swap_mdp have the two robustness gaps noted above.

Important Files Changed

Filename Overview
source/isaaclab_experimental/isaaclab_experimental/envs/frontend.py New 387-LOC adapter module; _walk_terms uses dir() instead of __dataclass_fields__, and _swap_mdp accesses stable.__name__ without an AttributeError guard.
scripts/reinforcement_learning/rsl_rl/train.py Adds --frontend {torch,warp} CLI arg with lazy optional import; render_mode threaded through correctly.
source/isaaclab/isaaclab/managers/manager_term_cfg.py Relaxes func return type to `torch.Tensor
source/isaaclab_experimental/isaaclab_experimental/managers/scene_entity_cfg.py Adds from_stable classmethod covering all current stable SceneEntityCfg fields.
source/isaaclab_experimental/test/envs/test_frontend.py 28 pure-Python unit tests; good coverage of walker, promotion, MDP swap, and direct-workflow guard.
source/isaaclab_experimental/isaaclab_experimental/managers/manager_term_cfg.py File deleted — now-redundant shim; all imports redirected to stable types.

Reviews (3): Last reviewed commit: "Rename CLI preset hint to newton_mjwarp" | Re-trigger Greptile

Comment on lines +256 to +267
def build(self, cfg: Any, task_id: str):
"""Adapt ``cfg`` and return a :class:`ManagerBasedRLEnvWarp` instance.

Always logs the missing-twin report (if any). With ``strict=True``,
any missing twin raises before the env is constructed.
"""
# Lazy: this is the first warp-lib load. Caller must already be inside
# the SimulationApp context, i.e. inside ``launch_simulation``.
from isaaclab_experimental.envs import ManagerBasedRLEnvWarp

self.adapt(cfg, task_id)
return ManagerBasedRLEnvWarp(cfg=cfg)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 WarpFrontend.build always constructs ManagerBasedRLEnvWarp with render_mode=None, but ManagerBasedRLEnvWarp.__init__ accepts render_mode and stores it. When --video is combined with --manager=warp, gym.wrappers.RecordVideo is applied to the returned env, but because render_mode was never set to "rgb_array", gymnasium will raise an error. The warp path silently drops the flag that the stable path (gym.make(..., render_mode="rgb_array" if args_cli.video else None)) correctly sets.

Suggested change
def build(self, cfg: Any, task_id: str):
"""Adapt ``cfg`` and return a :class:`ManagerBasedRLEnvWarp` instance.
Always logs the missing-twin report (if any). With ``strict=True``,
any missing twin raises before the env is constructed.
"""
# Lazy: this is the first warp-lib load. Caller must already be inside
# the SimulationApp context, i.e. inside ``launch_simulation``.
from isaaclab_experimental.envs import ManagerBasedRLEnvWarp
self.adapt(cfg, task_id)
return ManagerBasedRLEnvWarp(cfg=cfg)
def build(self, cfg: Any, task_id: str, render_mode: str | None = None):
"""Adapt ``cfg`` and return a :class:`ManagerBasedRLEnvWarp` instance.
Always logs the missing-twin report (if any). With ``strict=True``,
any missing twin raises before the env is constructed.
"""
# Lazy: this is the first warp-lib load. Caller must already be inside
# the SimulationApp context, i.e. inside ``launch_simulation``.
from isaaclab_experimental.envs import ManagerBasedRLEnvWarp
self.adapt(cfg, task_id)
return ManagerBasedRLEnvWarp(cfg=cfg, render_mode=render_mode)

# SimulationApp is already alive avoids racing pxr extension init.
from isaaclab_experimental.envs.warp_frontend import WarpFrontend

env = WarpFrontend().build(env_cfg, task_id=args_cli.task)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 The warp env is built without render_mode, so gym.wrappers.RecordVideo applied on line 222 will fail when --video --manager=warp is used. Pass the same render_mode the stable path uses.

Suggested change
env = WarpFrontend().build(env_cfg, task_id=args_cli.task)
env = WarpFrontend().build(env_cfg, task_id=args_cli.task, render_mode="rgb_array" if args_cli.video else None)

Comment on lines +256 to +267
def build(self, cfg: Any, task_id: str):
"""Adapt ``cfg`` and return a :class:`ManagerBasedRLEnvWarp` instance.

Always logs the missing-twin report (if any). With ``strict=True``,
any missing twin raises before the env is constructed.
"""
# Lazy: this is the first warp-lib load. Caller must already be inside
# the SimulationApp context, i.e. inside ``launch_simulation``.
from isaaclab_experimental.envs import ManagerBasedRLEnvWarp

self.adapt(cfg, task_id)
return ManagerBasedRLEnvWarp(cfg=cfg)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 build() discards the WarpAdaptReport returned by adapt(), so callers have no programmatic access to what was dropped. In the default non-strict mode, silently-dropped terms cause the env to train with a smaller effective term set than expected. A caller who wants to enforce a "fail if anything was dropped" policy must call adapt() separately, which then re-mutates the already-mutated cfg when build() calls it again internally.

Comment on lines +323 to +329
modules: list[ModuleType] = []
try:
import gymnasium as gym

entry = gym.spec(task_id).kwargs.get("env_cfg_entry_point")
except Exception:
entry = None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The bare except Exception swallows any error from gymnasium.spec(task_id) — including gymnasium.error.NameNotFound if the task ID is misspelled and real programming errors. When this fires, entry is set to None and only the fallback mdp module is used, so no task-specific warp twins are discovered, causing every term to go missing without any indication of why.

@hujc7
hujc7 marked this pull request as draft May 5, 2026 17:05
@hujc7 hujc7 changed the title Add WarpFrontend bridge: run any stable manager-based task on the warp runtime via --manager=warp [WARP] Add WarpFrontend bridge: run any stable manager-based task on the warp runtime via --manager=warp May 8, 2026

@isaaclab-review-bot isaaclab-review-bot Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Isaac Lab Review Bot — Re-review after Updates

Summary

This PR introduces a well-designed frontend framework (isaaclab_experimental.envs.frontend) that enables runtime backend selection for IsaacLab tasks via --frontend torch|warp. The architecture is clean: a Frontend base class with pluggable CompatRule pipelines, proper separation between task resolution (TaskResolver) and env construction, and clear error reporting via Report/Issue/Change records.

Since the initial review (May 5), the author has addressed all critical findings through commits 8934fe649a60 ("Tighten frontend framework against review findings") and 67050a863d5e ("Tighten frontend framework against second review pass"). The code quality has improved significantly.

Architecture Assessment

Strengths:

  • Clean plugin architecture: new compatibility rules are easy to add without modifying core framework
  • Proper error surfacing via structured Issue records with severity levels
  • Good idempotency handling in SwapMdpFunctions (skips already-warp terms)
  • Train script integration is backwards-compatible (falls back to gym.make when isaaclab_experimental isn't installed)
  • The manager_term_cfg.py simplification (re-exporting stable classes) correctly fixes the isinstance check issue

Design Decisions (reasonable but worth noting):

  • In-place cfg mutation rather than copy — documented and appropriate for the use case
  • frontend_report is set as a dynamic attribute on env.unwrapped — works but isn't declared on the env class

Implementation Verdict

Ship it ✅ — The previous critical issues have been resolved. The remaining items are minor improvements that don't block merging.

Resolved Issues from Previous Review

✅ issubclass(Warp, Stable) check now present (line 200)
✅ TypeError from __class__ promotion is caught and surfaced as blocking issue (line 225)
✅ Non-newton presets now emit a warning (lines 508-509)
✅ commands group added to both PromoteSceneEntityCfg and SwapMdpFunctions
✅ Module import errors now properly distinguish "module doesn't exist" from "module has internal error" (lines 338-343)

Remaining Findings

🔵 Improvement: No automated tests for the frontend framework

The PR adds substantial logic (rule pipeline, task resolution, cfg mutation) but no unit tests. The manual validation in the PR description is thorough, but automated tests would prevent regressions. Consider adding in a follow-up:

  • Unit tests for TaskResolver.resolve() with mock specs
  • Unit tests for each CompatRule with mock configs
  • Integration test for WarpFrontend.build() happy path

🔵 Improvement: base.py:365 — Consider declaring frontend_report attribute

The frontend_report attribute is set dynamically:

env.unwrapped.frontend_report = report

While this works, type checkers and IDE autocompletion won't know about it. Consider adding a type stub or documenting that users should expect this attribute.

🔵 Improvement: warp.py:165 — DropUnsupportedSensors could emit an Issue

Currently drops sensors silently (only emits a Change). A WARNING severity Issue might be more appropriate since dropping a sensor affects task behavior and users should be clearly informed.

CI Status

Checks are still pending. Recommend waiting for CI green before merge.

Test Coverage

The PR description includes thorough manual validation (Cartpole, Anymal-D Flat parity tests, negative cases). However, no automated tests are included for the new framework code. This is acceptable for an experimental package but should be addressed in a follow-up.


Re-reviewed at commit 429794db61ad after the refactoring commits addressed previous findings.


🤖 Update (e2cb744)

Major architectural simplification — The plugin-based CompatRule framework has been replaced with a much simpler functional approach:

Changes Summary

  1. Framework consolidation: ~1000+ lines across 4 files → ~373 lines in a single frontend.py module

    • Deleted: frontend/__init__.py, frontend/base.py, frontend/torch.py, frontend/warp.py
    • Added: frontend.py (flat module with direct functions)
  2. Behavioral changes:

    • presets=newton auto-injection removed — user must explicitly pass it (documented in changelog)
    • Missing MDP twins are now a hard failure (was WARNING/drop in non-strict mode) — this is safer
    • SceneEntityCfg.from_stable() classmethod replaces the __class__ reassignment hack
  3. Train script simplified: Direct build(frontend, env_cfg, task_id, render_mode=...) call

Previous Findings Status

✅ "No automated tests" — Now addressed! Comprehensive test_frontend.py added covering:

  • Enum surface tests
  • SceneEntityCfg.from_stable field copy
  • _require_newton_physics hard-check
  • _promote_scene_entity_cfgs walks term params
  • _swap_mdp func/class_type swap + missing twin error paths
  • Direct workflow guard tests

✅ frontend_report attribute — No longer applies (removed with the Report/Issue/Change system)

✅ DropUnsupportedSensors silent drop — Rule removed entirely; the new approach requires all twins to exist

Assessment

The simplification is a good architectural decision:

  • Easier to understand and maintain
  • Harder to misuse (fail-fast on missing twins)
  • Tests now included
  • The plugin extensibility wasn't being used and added complexity

Ship it ✅ — No new issues found in this update.


🤖 Update (bd27af4)

Dynamic term discovery — This update replaces hardcoded term path lists with recursive type-based discovery:

Changes Summary

  1. _walk_terms() refactoring: New recursive walker that discovers ManagerTermBaseCfg instances by type via isinstance(), not by hardcoded attribute names like ("observations", "policy"). This makes the framework future-proof — new observation groups, nested cfgs, or custom term containers are picked up automatically.

  2. Deleted manager_term_cfg.py: The experimental package no longer re-exports stable term cfgs; managers import directly from isaaclab.managers.manager_term_cfg. Cleaner dependency graph.

  3. Type hint updates: func callables in ObservationTermCfg, RewardTermCfg, TerminationTermCfg now allow | None return types, supporting warp-style out-parameter functions.

  4. Test improvements: Tests now use real @configclass fixtures instead of SimpleNamespace mocks, and explicitly verify that arbitrarily-named observation groups (e.g., perception) are discovered.

  5. Naming cleanup: _require_direct_is_warp_task → _assert_direct_warp_registration (clearer intent).

Assessment

This is a solid incremental improvement:

  • Dynamic discovery is more robust than hardcoded paths
  • Better test fixtures catch real configclass edge cases
  • Import simplification reduces surface area

Ship it ✅ — No new issues introduced.


🤖 Update (21e8bba)

Changelog housekeeping — Documentation-only commit:

  • Compressed isaaclab_experimental changelog fragment from 47 lines to 12 lines (follows terse-fragment convention)
  • Added isaaclab/changelog.d/warp-manager-bridge.rst documenting the Callable[..., torch.Tensor | None] type relaxation
  • Added isaaclab_tasks_experimental/changelog.d/warp-manager-bridge.skip (internal humanoid import redirect)

No code changes. Previous review findings unchanged.

Ship it ✅


🤖 Update (b932897)

Warp env API alignment — Syncs warp experimental envs with stable API changes:

Changes Summary

  1. direct_rl_env_warp.py:

    • Enabled ViewportCameraController (was commented out) — now initializes when Kit viewport exists
    • Added has_kit() guard to skip ViewportCameraController in kitless Newton-only runs
    • Replaced manual get_setting("/isaaclab/has_gui") calls with self.sim.has_gui / self.sim.has_offscreen_render properties
  2. manager_based_rl_env_warp.py:

    • Same render-check modernization: get_setting() → cached SimulationContext properties

These changes align with upstream stable PRs #4646 (cached render properties) and #5103 (visualizer API update).

Assessment

Clean sync with stable — No new issues. The inline comments from previous reviews (render_mode passthrough, exception handling) remain open but are in separate files not touched by this commit.

Ship it ✅


🤖 Update (d33a9bd)

Merge from develop + preset rename — Two branch-specific changes since b932897d:

  1. 0f344754 — Renames CLI preset hint from presets=newton to presets=newton_mjwarp in error messages, comments, and the corresponding test assertion. Cosmetic only; aligns with an upstream rename of the preset identifier.

  2. d33a9bdf — Merge of origin/develop into the feature branch (200+ upstream commits). No merge conflicts with the branch's own files; the diff is entirely upstream changes flowing in.

Previous Findings Status

All previous findings remain addressed. No regressions introduced.

Assessment

No new issues. The branch-specific delta is a trivial string rename that keeps documentation/error messages consistent with upstream preset naming. The develop merge is clean.

Ship it ✅


🤖 Update (d33a9bd, second pass)

✅ P1 fix confirmed: The ModuleNotFoundError check in _warp_mdp_modules (line 351) now uses warp_mod.startswith(exc.name) instead of the previous exc.name == target. This correctly handles the case where a parent package (e.g. isaaclab_tasks_experimental) is missing, not just the leaf module. The graceful fallback path now works as intended.

All inline comments from my previous review have been addressed.


🤖 Update (855588f)

Merge from develop (clean) — The new HEAD is a merge of origin/develop into the feature branch, incorporating ~50+ upstream commits (OvPhysX assets/sensors, Newton deformables, teleop replay MCAP migration, preset CLI improvements, visualizer fixes, packaging cleanup, etc.). No merge conflicts with the PR's own files. The branch-specific delta since d33a9bdf (last reviewed) is zero new feature code — the preset rename to newton_mjwarp (commit 0f344754) was already noted in the previous update.

Previous Findings Status

All previous findings remain addressed. No regressions introduced by the merge.

Assessment

No new issues. The branch is a clean forward-merge of upstream develop with no changes to the PR's own experimental frontend framework code.

Ship it ✅

@hujc7

hujc7 commented May 26, 2026

Copy link
Copy Markdown
Collaborator Author

@greptileai review

Comment on lines +308 to +318
for depth in range(len(parts), 0, -1):
target = ".".join(parts[:depth] + ["mdp"])
try:
modules.append(importlib.import_module(target))
break
except ModuleNotFoundError as exc:
# Only swallow "this candidate doesn't exist" — a real
# import error inside an existing module is a bug.
if exc.name == target:
continue
raise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 exc.name == target check is too strict — crashes when parent package is absent

ModuleNotFoundError.name is set to the specific module that was missing, which may be a parent package rather than the leaf target. For example, importing "isaaclab_tasks_experimental.locomotion.velocity.mdp" when isaaclab_tasks_experimental is not installed raises ModuleNotFoundError(name="isaaclab_tasks_experimental"). Since "isaaclab_tasks_experimental" != "isaaclab_tasks_experimental.locomotion.velocity.mdp", the condition is False and the exception is re-raised unconditionally — every depth in the walk fails the same way, so _warp_mdp_modules throws instead of falling back to the generic module. This turns the graceful "no task-specific twin module found" path into a hard crash any time isaaclab_tasks_experimental is not installed.

Change the guard to if exc.name and target.startswith(exc.name). This keeps the intent ("only swallow 'this candidate doesn't exist'") while correctly handling the case where a parent package is the missing link.

@hujc7
hujc7 marked this pull request as ready for review May 27, 2026 04:46
@hujc7 hujc7 added this to Isaac Lab May 27, 2026
@hujc7 hujc7 moved this to In review in Isaac Lab May 27, 2026
@hujc7
hujc7 requested a review from AntoineRichard June 1, 2026 16:19
@hujc7
hujc7 requested a review from kellyguo11 as a code owner June 9, 2026 06:17
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Jun 9, 2026

@AntoineRichard AntoineRichard left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High level review, i don't have a issue with the mechanical changes introduced in this PR. I think task owners will need to refactor these so they match their new tasks.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cartpole got completely rewritten by @StafaH we would need to match the new env exactly.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cartpole got completely rewritten by @StafaH we would need to match the new env exactly.

@hujc7 hujc7 changed the title [WARP] Add WarpFrontend bridge: run any stable manager-based task on the warp runtime via --manager=warp [WARP] Add frontend bridge for stable manager-based tasks Jul 4, 2026
@hujc7 hujc7 changed the title [WARP] Add frontend bridge for stable manager-based tasks [WARP] Cleanup Part 1: Add frontend bridge for stable manager-based tasks Jul 21, 2026
@hujc7
hujc7 requested review from StafaH and mmichelis as code owners July 22, 2026 07:02
Let the warp frontend adapt a stable manager-based configuration onto the
warp runtime without duplicating scene-entity configs.
@hujc7
hujc7 force-pushed the jichuanh/warp-manager-bridge branch from 67bcc4e to 00ae695 Compare July 23, 2026 23:11
hujc7 added 3 commits July 23, 2026 16:24
Introduce the WarpFrontend adapter that runs stable task ids on the warp
runtime: it adapts a stable manager-based cfg in place (swapping each MDP
term for its warp twin, resolved by name on the mirrored experimental
package), resolves direct env classes by name-based mirror, and converts
observation-noise cfgs to their warp-native twins. Adds the experimental
warp managers, environments, and noise utilities.
Lay out isaaclab_tasks_experimental as a mirror of isaaclab_tasks.core and
reuse the stable task configurations under --frontend warp. Direct warp envs
follow the <task>_warp_env / <Name>WarpEnv convention and resolve the
per-physics-backend joint gears; the retired -Warp-v0 registrations are
removed.
Add --frontend {torch,warp} to the shared reinforcement-learning training
and play entrypoints and construct environments through the frontend
factory.
@hujc7
hujc7 force-pushed the jichuanh/warp-manager-bridge branch 2 times, most recently from 69ba95d to 06072c5 Compare July 23, 2026 23:39
Comment thread docs/source/overview/environments.rst Outdated
@@ -640,8 +607,8 @@ Environments based on legged locomotion tasks.
.. |dr-legs-hold-pose-link| replace:: :isaaclab-source:`IsaacContrib-DrLegs-HoldPose <source/isaaclab_tasks/isaaclab_tasks/contrib/dr_legs/hold_pose_env_cfg.py>`
.. |dr-legs-walk-link| replace:: :isaaclab-source:`IsaacContrib-DrLegs-Walk <source/isaaclab_tasks/isaaclab_tasks/contrib/dr_legs/walk_env_cfg.py>`

.. |velocity-flat-digit-link| replace:: :isaaclab-source:`IsaacContrib-Velocity-Flat-Digit <source/isaaclab_tasks/isaaclab_tasks/contrib/velocity/config/digit/flat_env_cfg.py>`
.. |velocity-rough-digit-link| replace:: :isaaclab-source:`IsaacContrib-Velocity-Rough-Digit <source/isaaclab_tasks/isaaclab_tasks/contrib/velocity/config/digit/rough_env_cfg.py>`
.. |velocity-flat-digit-link| replace:: :isaaclab-source:`Isaac-Velocity-Flat-Digit <source/isaaclab_tasks/isaaclab_tasks/core/velocity/config/digit/flat_env_cfg.py>`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this change reverted?

Comment thread docs/source/overview/environments.rst Outdated
@@ -147,8 +127,8 @@ Classic environments that are based on IsaacGymEnvs implementation of MuJoCo-sty
| | | | ``newton_kamino`` |
+------------------+-----------------------------+-------------------------------------------------------------------------+------------------------------+
| |cartpole| | |cartpole-camera-presets| | Move the cart to keep the pole upwards in the classic cartpole control | **physics=** |
| | | and perceptive inputs. Select data type via ``presets=``. | ``physx``, |
| | | | ``newton_mjwarp``, |
| | | and perceptive inputs. Select data type via ``presets=``. Requires | ``physx``, |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--enable_cameras are removed

Comment thread docs/source/overview/environments.rst Outdated
To run inference on a trained task, pass its task name to ``play.py``. The play script applies the environment
configuration's ``play_mode`` overrides automatically, providing configurations more suitable for inferencing,
including disabling runtime perturbations used for training.
For environments that have a different task name listed under ``Inference Task Name``, please use the Inference Task Name

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inference task names are no longer applicable

@hujc7
hujc7 force-pushed the jichuanh/warp-manager-bridge branch from 06072c5 to 3fdef8b Compare July 24, 2026 05:03
kellyguo11 and others added 3 commits July 23, 2026 22:18
Document convention-based direct environment lookup and keep the
optional override and migration guidance consistent.
A stable direct env class can be shared by several tasks (e.g.
ReorientDirectEnv serves both the Allegro and Shadow hands via different
cfgs). Name-based resolution keys on that shared env class, so it mirrored
every sharing task to the single ReorientDirectWarpEnv twin -- which
implements only the Allegro variant -- silently running the wrong MDP for
Shadow under --frontend warp.

Have _resolve_direct_warp_class verify the name-mirrored twin implements
the task's cfg (via the twin's `cfg` annotation) before returning it; a
mismatch is a hard error. Explicit warp_entry_point overrides stay trusted.
The 4 supported direct tasks are unaffected; the 3 Shadow reorient tasks
now report unsupported instead of mis-resolving.
The warp reward/event managers accept a manager group expressed as a
{name: term} dict as well as a configclass, but _walk_terms stopped at any
non-configclass node -- so a dict group's terms were never discovered and
never swapped to their warp twins, leaving torch-signature callables that
fail under the warp managers.

Recurse into mapping values in _walk_terms so both group forms are covered.
No warp-supported task uses dict groups today; this closes the gap before
one does.
@kellyguo11
kellyguo11 merged commit 40d888c into isaac-sim:develop Jul 24, 2026
41 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in Isaac Lab Jul 24, 2026
matthewtrepte pushed a commit to matthewtrepte/IsaacLab that referenced this pull request Aug 4, 2026
…asks (isaac-sim#5504)

## Review Map

- A stacked PR’s page shows the cumulative diff of its dependency chain;
the "Exact changes" link pins that PR’s own commit range.
- SHA links can go stale after a branch update; the copy on isaac-sim#5504 is
refreshed first.

| PR | Status | Depends on | Exact changes |
|---|---|---|---|
| 📌 isaac-sim#5504 Part 1: frontend bridge (stable ids on the warp runtime) |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/5504?label=)
| — | — |
| isaac-sim#6607 Part 2: mask-first frontend (draft) |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6607?label=)
| isaac-sim#5504 |
[link](https://github.com/isaac-sim/IsaacLab/pull/6607/changes/00ae695ac63e67ca678b91a837d3737529d2ec91..f68d03f40854b31a36063c5068f2933d32ea5db9)
|
| isaac-sim#6646 Part 3: scene reset mask-native + capture-ready (draft, base
rewritten — pending rebase onto isaac-sim#6607) |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6646?label=)
| isaac-sim#6607 |
[link](https://github.com/isaac-sim/IsaacLab/pull/6646/changes/4360622bde446c52c3fc85924639f9d1119d6e95..6d49e32f11e15894a4a0e49161a356ec79679ac6)
|
| isaac-sim#6580 Part 4: OVPhysX backend (draft) |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6580?label=)
| isaac-sim#5504 |
[link](https://github.com/isaac-sim/IsaacLab/pull/6580/changes/fc37fa65ad95d1ac6e3f32e721329ef715899d37..716cef20979ca6c578a6e595812291c7b1a282fc)
|
| isaac-sim#6611 Part 5: persistent-launch speedups (draft, absorbs isaac-sim#6551) |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6611?label=)
| isaac-sim#6646 |
[link](https://github.com/isaac-sim/IsaacLab/pull/6611/changes/6d49e32f11e15894a4a0e49161a356ec79679ac6..6e3da794215da03745a9bc8d35790e22262b099a)
|

## 1. Summary

- `--frontend {torch,warp}` on the shared RL CLI (train **and play**)
runs **stable** task ids on the warp runtime — no duplicated configs, no
parallel warp task registrations at all (end state pinned by test). The
`rlinf` integration constructs environments inside the external
framework and is not frontend-routable (known limitation).
- **1.24–1.70× faster end-to-end** than torch on the same tasks,
identical Newton physics (§3.1).
- Manager-based: `WarpFrontend` adapts the stable cfg in place; MDP
twins resolve by name on the mirrored experimental package tree —
deterministic, registration-free, missing twins reported in one
aggregated failure.
- Direct: the stable registration declares its warp class via
`warp_entry_point`; only the env class swaps, the cfg is shared.
- Stable observation-noise cfgs (constant/uniform/gaussian) swap to
warp-native twins during adaptation; cfgs without a twin (class-based
noise models, customized funcs, tensor-valued params) are a hard error
instead of silently dropping noise. The warp observation manager rejects
non-warp noise cfgs and plumbs the shared per-env RNG state.
- Plus: warp adapters for the material/mass randomization startup
events; `isaaclab_tasks_experimental` mirrors the stable `core/<task>`
layout; `Metrics/success_rate` restored under warp.

## 2. Migration map

| Retired | Replacement |
|---|---|
| `Isaac-{Cartpole,Humanoid,Ant}-Warp-v0`, `Isaac-Reach-Franka-Warp-v0`
(+`-Play`) | stable id + `--frontend warp presets=newton_mjwarp` |
| `Isaac-Velocity-Flat-<Robot>-Warp-v0` (+`-Play`), all robots | stable
id + `--frontend warp presets=newton_mjwarp` |
| `Isaac-{Cartpole,Ant,Humanoid}-Direct-Warp-v0`,
`Isaac-Reorient-Cube-Allegro-Direct-Warp-v0` | stable id + `--frontend
warp presets=newton_mjwarp` (via `warp_entry_point`) |

## 3. Test plan

- [x] Merged current `develop`; `./isaaclab.sh -f` clean on all files.
- [x] Re-merged develop (task-id catalog update isaac-sim#6606, entrypoints
refactor); environments catalog regenerated; conversion sweep + warp
smokes (`Isaac-Velocity-Flat-UnitreeGo2`, `Isaac-Cartpole-Direct`)
re-run green; pre-commit clean on all files.
- [x] 142 focused tests: frontend units, cfg-conversion sweep over every
covered stable id, no-warp-registrations end-state pin, shared-CLI
tests.
- [x] 5-iteration RSL-RL trainings exit clean (`Training time:` present,
no traceback): `Isaac-Cartpole` (torch); `Isaac-Cartpole`,
`Isaac-Humanoid`, `Isaac-Cartpole-Direct`, `Isaac-Ant-Direct`,
`Isaac-Velocity-Flat-AnymalD`, `Isaac-Velocity-Flat-H1` (warp;
randomization events active); `Isaac-Reorient-Cube-Allegro-Direct`
(warp). `Metrics/success_rate` reported under warp.

### 3.1 Frontend A/B benchmark

Same stable task id and cfg on both sides, `presets=newton_mjwarp` on
**both** (identical Newton physics — only `--frontend` differs); rsl_rl,
4096 envs, 300 iterations, headless, one L40 per run, single run per
cell. Metric: rsl_rl `Training time:` — learn-loop wall-clock (env
stepping + PPO update; warp includes its one-time CUDA-graph capture).
PPO-update cost is common to both, so the env-pipeline gain exceeds the
end-to-end ratio.

| Task | torch | warp | end-to-end | final reward (torch / warp) |
|---|---|---|---|---|
| `Isaac-Cartpole` | 58.2 s | 39.7 s | 1.46× | 4.95 / 4.95 |
| `Isaac-Humanoid` | 162.0 s | 110.5 s | 1.47× | 149.1 / 179.8 |
| `Isaac-Ant` | 109.8 s | 64.5 s | 1.70× | 82.1 / 77.1 |
| `Isaac-Velocity-Flat-AnymalD` | 162.4 s | 130.7 s | 1.24× | 20.3 /
21.4 |

## 4. Out of scope / follow-ups

- No video-recorder plumbing on warp envs; rough terrain blocked on Warp
`TerrainImporter` APIs; no twins for IK/OSC actions, `height_scan`, or
Digit/Spot-specific terms.
- OVPhysX support for the warp frontend prepared as a follow-up PR
(physics gate widening + backend data-parity test).
- Candidate refactor: fold the ~300 lines of converged plumbing shared
by `DirectRLEnvWarp` and stable `DirectRLEnv` into a common base.

---------

Co-authored-by: Kelly Guo <kellyguo123@hotmail.com>
kellyguo11 pushed a commit that referenced this pull request Aug 13, 2026
…et in CI (#6900)

## 1. Summary

- `--frontend warp` fails at env construction for `Isaac-Reach-Franka`
and `Isaac-Reach-UR10`: `rewards.success` and `terminations.success`
resolve to `is_terminated_term` and `pose_command_success`, which had no
warp twins. Adds both, matched to the stable terms and validated by
training.
- The warp-supported task list was hand-maintained and had already
drifted — sweeping the registry finds 4 contrib velocity tasks that
adapt but were never listed (10 → 14). It is now derived, not declared.
- Adds a **CUDA-graph capture-safety gate** for warp MDP terms. Managers
run captured by default, so capture safety is the normal path; the gate
captures a term, overwrites its inputs in place, replays, and compares
against stable on the new data.
- `source/isaaclab_experimental/` had no CI job at all: all 9 of its
test files were collected by nothing. Test files matched by no job: 13 →
4.
- Also restores `Isaac-Ant` / `Isaac-Humanoid`, which lost warp support
on develop when the locomotion rewards gained `terminated_penalty` and
`survival_success_rate` without twins — the sweep in this PR is what
surfaced it.
- Package suite: 203 passed, 1 skipped, ~10 s. NVBug 6558734.

## 2. Why the existing gate did not catch it

`test_frontend_cfg_conversion.py` already asserted both Reach tasks
adapt, and has since #5504 — it just never ran. Jobs either name a
package with a positive `filter-pattern`, or pass `not isaaclab_`, which
`run_tests.sh` converts into an exclude on `isaaclab_`. This package
matched neither form.

## 3. Twin semantics

`pose_command_success` recomputes the pose error in a kernel rather than
copying the stable result, whose buffer is reallocated per call and
would go stale under graph replay. Three points where a naive twin
diverges from the stable term, each with a regression test:

| Behaviour | Stable | Naive twin |
|---|---|---|
| Sticky per-episode success | ORed inside `compute_success()`, before
reset | never set; `reset()` reads and clears first, so
`Metrics/success_rate` reads 0 |
| Non-finite error | `error < bound` is false → denies success | reject
form `error >= bound` is false → **reports success** |
| Negative threshold | configured (`is not None`) → denies success |
read as "unset" → ignored |

The sticky write happens inside the kernel, not after the launch:
host-side work between launches is not replayed under capture.

Thresholds are resolved at init. A curriculum mutating them at runtime
is not honoured, and cannot be while the manager is captured — a kernel
scalar is baked into the graph. Recorded in the class docstring.

## 4. Capture-safety gate

Terms are discovered from the shared warp MDP modules **and every
per-task mirror** under `isaaclab_tasks_experimental`, rather than
listed, so a new term without a declaration fails the gate. Enumerating
separated three groups convention had blurred: terms already covered by
the hand-written mutation classes, three observation terms with no
capture coverage anywhere, and eight event terms that cannot be captured
at all (variable `env_ids` launch dim at reset cadence).

Capture-and-replay alone does not prove safety — replaying against
unchanged memory re-reads the same valid bytes. Injecting a stale-view
bug (`wp.clone` instead of a zero-copy view) fails only
`test_term_is_capture_safe[pose_command_success]`; all 17 other tests
pass.

## 5. Test plan

- [x] `pytest source/isaaclab_experimental/test/` — 201 passed, 1
skipped
- [x] Reverting the twins fails the sweep, naming both tasks and both
missing symbols
- [x] Mutation checks: stale view → only the capture test fails;
reject-form predicate → only the NaN test; dropped sticky write → the
threshold tests; wxyz quaternion → only the orientation cases
- [x] Training, 4096 envs / 150 iters / `--frontend warp
presets=newton_mjwarp`:

| Task | Training time | Reward start → end | `Metrics/success_rate` |
|---|---|---|---|
| `Isaac-Reach-Franka` | 91.76 s | −0.63 → −0.49 | 0.78 |
| `Isaac-Reach-UR10` | 72.25 s | −2.43 → 0.03 | 0.99 |

With the sticky write reverted, Franka trains identically (episode
length still falls 360 → 252, so terminations fire) but
`Metrics/success_rate` stays 0.0000 for all 150 iterations — the metric
bug in isolation.
- [x] `uv run isaaclab -f` clean

## 6. Out of scope

- `source/isaaclab_ppisp/` has the same CI hole (4 uncollected test
files).
- Three observation terms have no capture coverage yet: `joint_pos_rel`,
`joint_vel_rel`, `body_incoming_wrench`.
- `isaaclab_tasks_experimental` has no test directory, so its twenty
pre-existing per-task twins have no parity or capture coverage anywhere.
They are recorded in the gate's backlog rather than assumed correct.
- Warp orientation-error twins use `2*acos(|w|)` where the stable path
normalizes first; the two differ by ~7e-4 rad at float32, far below any
configured threshold. Pre-existing in `orientation_command_error`; left
consistent rather than changed in one place.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation isaac-lab Related to Isaac Lab team

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants