Skip to content

fix(whole-body): discover sim adapters in registry#2653

Open
KrishnaH96 wants to merge 3 commits into
mainfrom
fix/whole-body-sim-adapter-discovery
Open

fix(whole-body): discover sim adapters in registry#2653
KrishnaH96 wants to merge 3 commits into
mainfrom
fix/whole-body-sim-adapter-discovery

Conversation

@KrishnaH96

Copy link
Copy Markdown
Contributor

What

In dimos/hardware/whole_body/registry.py, made discover() also scan the sim adapter root (dimos/simulation/adapters/whole_body/) via a new _discover_flat() helper — so flat <name>.py adapter modules get imported and registered. Graceful ImportError skip so hardware-only installs are unaffected. (~20 lines, registry.py only.)

Why

discover() only scanned dimos/hardware/whole_body/, so the sim adapter sim_mujoco_g1 was never registered — dimos --simulation mujoco run unitree-g1-groot-wbc crashed with KeyError: Unknown whole-body adapter: sim_mujoco_g1. The registry's own docstring already says it should scan both roots, so this makes the code match the documented intent. (Same fix Pim intended in the closed part-3 PR #2225, isolated as a surgical change.)

How tested

  • Repro → fixed: before, the command crashed at adapter lookup; after, it runs end-to-end — SimMujocoG1WholeBodyAdapter connected, GR00T policy armed, TickLoop started at 50.0Hz, robot pose rendering in the viewer.
  • No regression: hardware adapters still register (the transport_lcm/transport_ros path is untouched and ordered first).
  • Gates green: ruff check + ruff format --check pass; mypy clean on the file.

Contributor License Agreement

  • I have read and approved the CLA.

registry.discover() only scanned dimos/hardware/whole_body, so the sim
adapter sim_mujoco_g1 was never imported/registered and
`dimos --simulation mujoco run unitree-g1-groot-wbc` raised
"Unknown whole-body adapter: sim_mujoco_g1". Also scan
dimos/simulation/adapters/whole_body (flat modules), which the registry
docstring already documents. ImportError is handled gracefully so
hardware-only installs are unaffected.
@KrishnaH96 KrishnaH96 requested a review from Nabla7 June 29, 2026 19:10
@greptile-apps

greptile-apps Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes the whole-body adapter registry so discover() also scans dimos/simulation/adapters/whole_body/ for flat <name>.py adapter modules, resolving the KeyError: Unknown whole-body adapter: sim_mujoco_g1 crash. The existing registry docstring already described this dual-root intent; the code now matches it.

  • Adds a _discover_flat() helper that walks a directory for top-level .py files and calls their register() function, mirroring the existing _discover_in() pattern used for hardware adapters.
  • Correctly iterates all entries in sim_pkg.__path__ (instead of indexing [0]) so the discovery works properly with namespace packages that may span multiple source trees.
  • Guards the sim package import with except ImportError so hardware-only installs are unaffected.

Confidence Score: 4/5

Safe to merge for the targeted bug fix; the one open concern from a prior thread — that mod.register(self) in _discover_flat is unguarded against non-ImportError exceptions — remains unaddressed but is low-probability given current adapter implementations.

The change is small, surgical, and solves the documented crash. The namespace-package __path__ iteration is handled correctly on the new sim side. The lingering concern is that a future adapter whose register() raises something other than ImportError (e.g. a ValueError or RuntimeError from a bad default) would propagate all the way out and break module-level registry initialization for every consumer — a blast radius disproportionate to the failure. The current g1.py adapter's register() is just a registry.register() call and cannot throw, so there is no immediate breakage.

dimos/hardware/whole_body/registry.py — specifically the unguarded mod.register(self) call in _discover_flat (line 116).

Important Files Changed

Filename Overview
dimos/hardware/whole_body/registry.py Adds sim adapter discovery via a new _discover_flat() helper and correctly iterates all sim_pkg.__path__ entries for namespace-package safety. The mod.register(self) call inside _discover_flat (line 116) sits outside any exception handler, so a non-ImportError raised by a future adapter's register() will propagate and crash module-level initialization — noted in a prior review thread.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["whole_body_adapter_registry.discover()"] --> B["import dimos.hardware.whole_body"]
    B --> C["_discover_in('dimos.hardware.whole_body', hw_pkg.__path__[0], max_depth=2)"]
    C --> D{entry is dir\nnot _/. prefixed?}
    D -- yes --> E["importlib.import_module(sub_pkg.adapter)"]
    E -- ImportError & depth > 1 --> C
    E -- ImportError & depth == 1 --> F["log warning, skip"]
    E -- success --> G["mod.register(registry)"]
    A --> H{"import dimos.simulation\n.adapters.whole_body"}
    H -- ImportError --> I["log warning, return"]
    H -- success --> J["for sim_root in sim_pkg.__path__"]
    J --> K["_discover_flat('dimos.simulation.adapters.whole_body', sim_root)"]
    K --> L{entry ends .py\nnot _/. prefixed?}
    L -- no --> K
    L -- yes --> M["importlib.import_module(pkg_path.module)"]
    M -- ImportError --> N["log warning, continue"]
    M -- success --> O["mod.register(registry)"]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["whole_body_adapter_registry.discover()"] --> B["import dimos.hardware.whole_body"]
    B --> C["_discover_in('dimos.hardware.whole_body', hw_pkg.__path__[0], max_depth=2)"]
    C --> D{entry is dir\nnot _/. prefixed?}
    D -- yes --> E["importlib.import_module(sub_pkg.adapter)"]
    E -- ImportError & depth > 1 --> C
    E -- ImportError & depth == 1 --> F["log warning, skip"]
    E -- success --> G["mod.register(registry)"]
    A --> H{"import dimos.simulation\n.adapters.whole_body"}
    H -- ImportError --> I["log warning, return"]
    H -- success --> J["for sim_root in sim_pkg.__path__"]
    J --> K["_discover_flat('dimos.simulation.adapters.whole_body', sim_root)"]
    K --> L{entry ends .py\nnot _/. prefixed?}
    L -- no --> K
    L -- yes --> M["importlib.import_module(pkg_path.module)"]
    M -- ImportError --> N["log warning, continue"]
    M -- success --> O["mod.register(registry)"]
Loading

Reviews (2): Last reviewed commit: "Update dimos/hardware/whole_body/registr..." | Re-trigger Greptile

Comment on lines +105 to +115
def _discover_flat(self, pkg_path: str, dir_path: str) -> None:
for entry in sorted(os.listdir(dir_path)):
if not entry.endswith(".py") or entry.startswith(("_", ".")):
continue
try:
mod = importlib.import_module(f"{pkg_path}.{entry[:-3]}")
except ImportError as e:
logger.warning(f"Skipping whole-body adapter {entry}: {e}")
continue
if hasattr(mod, "register"):
mod.register(self)

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 per-module except ImportError in _discover_flat will not catch Exception subclasses raised by a broken mod.register(self) call. If any sim adapter's register() raises (e.g., a constructor call with a bad default), the exception propagates out of _discover_flat, out of discover(), and ultimately fails the module-level import of registry.py — crashing the whole application. Consider a guarded call here, consistent with the "graceful skip" intent of the rest of the method.

Suggested change
def _discover_flat(self, pkg_path: str, dir_path: str) -> None:
for entry in sorted(os.listdir(dir_path)):
if not entry.endswith(".py") or entry.startswith(("_", ".")):
continue
try:
mod = importlib.import_module(f"{pkg_path}.{entry[:-3]}")
except ImportError as e:
logger.warning(f"Skipping whole-body adapter {entry}: {e}")
continue
if hasattr(mod, "register"):
mod.register(self)
if hasattr(mod, "register"):
try:
mod.register(self)
except Exception as e: # noqa: BLE001
logger.warning(f"Skipping whole-body adapter {entry} (register() failed): {e}")

Comment thread dimos/hardware/whole_body/registry.py Outdated
@codecov

codecov Bot commented Jun 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

@@           Coverage Diff           @@
##             main    #2653   +/-   ##
=======================================
  Coverage   71.10%   71.10%           
=======================================
  Files         897      897           
  Lines       80290    80290           
  Branches     7183     7183           
=======================================
+ Hits        57088    57090    +2     
+ Misses      21319    21318    -1     
+ Partials     1883     1882    -1     
Flag Coverage Δ
OS-ubuntu-24.04-arm 63.51% <ø> (ø)
OS-ubuntu-latest 66.23% <ø> (-0.01%) ⬇️
Py-3.10 66.22% <ø> (-0.01%) ⬇️
Py-3.11 66.22% <ø> (-0.01%) ⬇️
Py-3.12 66.22% <ø> (-0.01%) ⬇️
Py-3.13 66.22% <ø> (-0.01%) ⬇️
Py-3.14 66.24% <ø> (ø)
Py-3.14t 66.23% <ø> (+<0.01%) ⬆️
SelfHosted-Large 30.12% <ø> (+0.02%) ⬆️
SelfHosted-Linux 37.48% <ø> (ø)
SelfHosted-macOS 36.31% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.
see 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@greptile-apps

greptile-apps Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Want your agent to iterate on Greptile's feedback? Try greploops.

@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Required CI checks have passed on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant