Skip to content

Adventure x#3138

Draft
Nabla7 wants to merge 56 commits into
mainfrom
adventure_x
Draft

Adventure x#3138
Nabla7 wants to merge 56 commits into
mainfrom
adventure_x

Conversation

@Nabla7

@Nabla7 Nabla7 commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Contribution path

  • Small, safe change that does not need a tracking issue
  • Linked issue or discussion: DIM-XXX / #XXX / URL

Problem

Solution

How to Test

AI assistance

Checklist

  • I have read and approved the CLA.

jetsonearth and others added 30 commits July 15, 2026 00:13
GalaxeaA1ZAdapter implements the ManipulatorAdapter protocol on top of the
vendor a1z SDK (CAN bus, MIT PD + gravity comp at 250 Hz). POSITION
(min-jerk planned moves) and SERVO_POSITION (streaming) control modes,
latching soft e-stop, FK-based cartesian reads, G1Z gripper support
(requires the vendor SDK's 'gripper' branch; meters API, 10 cm measured
stroke), and teach-and-play recording exposed as adapter extensions.

Startup is a verified zero-force sequence: motors enable at kp=0, every
motor must report fresh in-limit feedback before hold gains engage at the
measured pose - the vendor's stock start() position-holds a single 50 ms
read and snaps the arm to zero if feedback is late (reproduced on
hardware). Shutdown re-sends disable frames to all motors including the
gripper, whose single vendor disable frame can be lost on a busy bus.
Opt out with safe_start=False for vendor-stock behavior.

Hardware-validated on a physical A1Z + G1Z.
…sport

GsUsbMacBus is a python-can BusABC driving the bundled HHS USB-CANFD
adapter over libusb - no SocketCAN, no Linux host. The adapter selects it
automatically on macOS (transport="auto"); Linux keeps socketcan.

Handles this device's quirks: TX endpoint discovered from descriptors
(device uses 0x01, gs_usb lib assumes 0x02), no-op kernel-driver detach,
TX echo filtering, RX queue flush on open (the device retains stale
frames across sessions, which parse as garbage feedback), and up-to-5s
discovery retry (the device drops off the USB bus briefly after a close).

Validated on an M4 Pro against a live A1Z: 30 s sustained 250 Hz control
loop (p99 cycle 5.2 ms, 0/7500 over the SDK's 12.5 ms watchdog limit),
~100% feedback from all 7 motors. Requires pyusb + gs_usb + libusb.
coordinator-galaxea-a1z runs the arm under the ControlCoordinator with a
trajectory task. Hardware-certified end to end: coordinator boot,
client-submitted multi-joint trajectories over LCM RPC, 0.07 rad return
drift.

Defaults to the stable arm-only configuration (SDK main branch). The
vendor gripper branch ships a G1Z gravity model that mismatches at least
some mountings - it pushes the arm during the zero-force startup window,
and disabling gravity compensation as a workaround leaves the vendor's
soft e-stop unable to catch the arm. Flip gripper=True after gravity/
zero-point calibration (vendor tools/set_zero.py).
…dware

Wire the galaxea_a1z hardware adapter into the a1z planning model that
landed on main (dimos/robot/manipulators/a1z): a planner+coordinator
blueprint for ManipulationModule-driven motion and a keyboard teleop
blueprint using the eef twist task. Both run the arm-only stable
configuration (gripper=False, A1Z_Flange model) consistent with
coordinator-galaxea-a1z; the mock-based a1z-* blueprints are untouched.
…available

The A1Z ships with an HHS USB-CANFD adapter that the kernel gs_usb
driver cannot actually drive: the adapter's VID/PID (a8fa:8598) is not
in the driver's device table, and its bulk OUT endpoint is 0x01 where
kernels before 6.x hardcode 0x02, so every transmit fails even if the
driver is force-bound. On such machines (found on a JetPack 6 Jetson,
kernel 5.15) the arm is unreachable over socketcan no matter what,
even though the adapter is plugged in and healthy.

Teach transport="auto" to handle this: keep socketcan whenever the
channel exists and is up, so stock kernels that bind the adapter keep
working exactly as before, and only otherwise fall back to the
userspace gs_usb bus already used on macOS, with a log line saying so.
A channel that merely exists is not enough to pick socketcan: an
unrelated on-board CAN controller can expose can0 with nothing wired
to it, which is exactly what the Jetson does.

Verified on hardware: zero-force scan reads all 7 motors, the
coordinator blueprint boots and holds pose, and the first-trajectory
script passes with 0.066 rad settled drift.
Keep the gripper powered during hand-teaching and toggle it open/closed
with a single g keypress, so the operator's hand stays out of the wrist
camera footage. The legacy pinch-by-hand behavior remains available via
--gripper-free-drive. Prompts now read single keypresses (cbreak) with a
line-based fallback for non-tty stdin.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The HHS adapter is a Full-Speed USB device read one frame per libusb
round-trip. Draining it synchronously from the SDK's 250 Hz control
loop (~1 ms budget per cycle) cannot sustain the ~3500 frames/s the
bus carries (7 motors' feedback plus TX echoes), so the device FIFO
overflowed and feedback froze for 0.3-2.4 s at a time - poisoning
teach recordings with stale-then-jump positions and tripping the
SDK's stale-feedback watchdog during replay.

A dedicated reader thread now drains USB continuously (libusb
releases the GIL while blocked), filters TX echoes, and queues real
frames; recv() becomes an instant pop that always meets the SDK's
drain budget. Validated on hardware: teach recordings are freeze-free
and replay runs without stale warnings.
Two changes so a hand-taught demonstration replays at 1.0x instead of
being silently time-scaled to a crawl:

- Smooth on the uniform resampled grid instead of the raw samples.
  The recorder's sample spacing is irregular (10-100 ms under load);
  differentiating linear interpolation across those gaps manufactured
  acceleration spikes an order of magnitude above the real motion
  (74 rad/s^2 measured vs ~14 real), which throttled the safety
  time-scaler and put velocity ripple into the streamed commands.
  Two moving-average passes give a continuous velocity profile.

- Size the replay caps to measured reality: natural hand teaching
  peaks ~3.5 rad/s (old cap 1.5 rescaled every demo), staying under
  the SDK's 4.0 rad/s streaming cap and 7-20 rad/s watchdogs. The
  gripper is commanded through the SDK's own controller (measured
  ~0.28 m/s, ~9 m/s^2), so its caps now cover the SDK's own motion.

Validated on hardware: a 33.5 s natural-speed episode preflights at
1.00x and replays smoothly at 0.5x and 1.0x.
Single-purpose keys with a persistent status line: SPACE starts and
saves episodes, g toggles the gripper, d discards, q quits with a
save/discard confirmation when an episode is in progress.

Denoise the session by fixing root causes: publish the camera transform
immediately at start (first-second frames also gain poses), rate-limit
the recorder's poseless-message warning, drop in-flight recording
callbacks during shutdown to avoid the closed-database error, and treat
disabling an already-disconnected A1Z adapter as success.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ruthwikdasyam and others added 20 commits July 21, 2026 06:53
Add local_only (default true) so multicast scouting is pinned to the
loopback interface and listen endpoints stay on localhost. Prevents two
machines on the same LAN from discovering each other and fighting over
shared topic names.
Review feedback on the manipulation docs: a new developer cannot tell
what DimOS offers for manipulation or how to build an application with
it. The index was a feature braindump leading with keyboard teleop; the
learning stack (the meat) was absent from Mintlify entirely; per-arm
integration write-ups sat in the capability section.

Restructured around what a developer wants to do:

- index.md — rewritten. What the stack is, a choose-your-workflow table,
  the three layers, and why the planning layer is where your time goes.
  Two-minute quickstart against the mock arm.

- planning.md (new) — the planning stack: how a pose target becomes
  motion, the plan/preview/execute API, backend selection, and how to
  plug in your own planner or IK solver (protocol + factory, no
  registry). Ends with where learned policies fit: a policy replaces the
  planner, feeding a servo task instead of a trajectory task.

- learning.md (new) — the full loop on one page: demonstrate (Quest VR
  collect or A1Z hand-teach) → session.db → dataprep (LeRobot v3/HDF5)
  → lerobot-train → run the policy on the robot. Every command verified
  against this branch, including the new a1z teach/replay/run-policy CLI
  and undo-last-episode.

- a750.md and openarm_integration.md moved to platforms/arms/ — they are
  platform setup docs, not capability docs. Redirects added.

VR teleop and data collection are now first-class in the manipulation
docs (learning.md) instead of hidden in dimos/teleop/quest/README.md.
Zenoh peers discover each other via multicast scouting, which is
bidirectional and (with a tailscale 224.0.0/4 route) leaks across the
tailnet, so separate machines collide on shared topics.

Hash a per-machine domain (default: hostname) to a multicast port so each
machine lands on its own discovery port. Same-machine processes share the
port and still auto-discover; different machines never see each other's
traffic. Set DIMOS_ZENOH_DOMAIN to the same value on machines that should
share (like ROS_DOMAIN_ID). Multicast stays on; no coordinator changes.
Peers only form a transport link when they share the secret, so other
machines on the network can't see or collide with this host's zenoh
traffic. The password defaults to the machine id (derivable by any
on-machine process, so local peers auto-share) and can be overridden
with DIMOS_ZENOH_PASSWORD.
@mintlify

mintlify Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
dimensional 🟢 Ready View Preview Jul 22, 2026, 9:15 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

def _password_dictionary_file(user: str, password: str) -> str:
directory = CACHE_DIR / "zenoh_auth"
directory.mkdir(parents=True, exist_ok=True)
digest = hashlib.sha256(f"{user}:{password}".encode()).hexdigest()[:16]
directory.mkdir(parents=True, exist_ok=True)
digest = hashlib.sha256(f"{user}:{password}".encode()).hexdigest()[:16]
path = directory / f"{digest}.txt"
path.write_text(f"{user}:{password}\n")
@mintlify

mintlify Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
dimensional 🟡 Building Jul 22, 2026, 9:14 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@codecov

codecov Bot commented Jul 22, 2026

Copy link
Copy Markdown

❌ 4 Tests Failed:

Tests completed Failed Passed Skipped
2758 4 2754 165
View the top 3 failed test(s) by shortest run time
::dimos.robot.manipulators.galaxea_a1z.test_teach_replay
Stack Traces | 0s run time
.../manipulators/galaxea_a1z/test_teach_replay.py:31: in <module>
    from dimos.robot.manipulators.galaxea_a1z.blueprints.basic import (
        CameraModule = <class 'dimos.hardware.sensors.camera.module.CameraModule'>
        CollectionRecorder = <class 'dimos.learning.collection.recorder.CollectionRecorder'>
        ControlCoordinator = <class 'dimos.control.coordinator.ControlCoordinator'>
        Episode    = <class 'dimos.learning.dataprep.core.Episode'>
        EpisodeStatus = <class 'dimos.learning.collection.episode_monitor.EpisodeStatus'>
        JointState = <class 'dimos.msgs.sensor_msgs.JointState.JointState'>
        LeRobotPolicyConfig = <class 'dimos.learning.lerobot_policy.LeRobotPolicyConfig'>
        LeRobotPolicyModule = <class 'dimos.learning.lerobot_policy.LeRobotPolicyModule'>
        Path       = <class 'pathlib.Path'>
        SqliteStore = <class 'dimos.memory2.store.sqlite.SqliteStore'>
        Webcam     = <class 'dimos.hardware.sensors.camera.webcam.Webcam'>
        __builtins__ = <builtins>
        __cached__ = '.../galaxea_a1z/__pycache__/test_teach_replay.cpython-312.pyc'
        __doc__    = None
        __file__   = '.../work/dimos/dimos/.../manipulators/galaxea_a1z/test_teach_replay.py'
        __loader__ = <_pytest.assertion.rewrite.AssertionRewritingHook object at 0xff8647622600>
        __name__   = 'dimos.robot.manipulators.galaxea_a1z.test_teach_replay'
        __package__ = 'dimos.robot.manipulators.galaxea_a1z'
        __spec__   = ModuleSpec(name='dimos.robot.manipulators.galaxea_a1z.test_teach_replay', loader=<_pytest.assertion.rewrite.AssertionR...t at 0xff8647622600>, origin='.../work/dimos/dimos/.../manipulators/galaxea_a1z/test_teach_replay.py')
        annotations = _Feature((3, 7, 0, 'beta', 1), None, 16777216)
        np         = <module 'numpy' from '.../dimos/dimos/.venv/lib/python3.12.../site-packages/numpy/__init__.py'>
        pytest     = <module 'pytest' from '.../dimos/dimos/.venv/lib/python3.12.../site-packages/pytest/__init__.py'>
.../galaxea_a1z/blueprints/basic.py:43: in <module>
    _A1Z_DYNAMICS_URDF = str(A1Z_G1Z_MODEL_PATH)
        A1Z_G1Z_MODEL_PATH = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempt...a/.lfs/a1z_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff854f0a5ad0>
        A1Z_POLICY_TASK_NAME = 'lerobot_servo_arm'
        A1Z_REPLAY_TASK_NAME = 'teach_replay_arm'
        A1Z_TEACH_CAMERA_FPS = 15.0
        A1Z_TEACH_CAMERA_HEIGHT = 480
        A1Z_TEACH_CAMERA_WIDTH = 640
        Blueprint  = <class 'dimos.core.coordination.blueprints.Blueprint'>
        CameraModule = <class 'dimos.hardware.sensors.camera.module.CameraModule'>
        CollectionRecorder = <class 'dimos.learning.collection.recorder.CollectionRecorder'>
        ControlCoordinator = <class 'dimos.control.coordinator.ControlCoordinator'>
        EpisodeMonitorModule = <class 'dimos.learning.collection.episode_monitor.EpisodeMonitorModule'>
        LeRobotPolicyConfig = <class 'dimos.learning.lerobot_policy.LeRobotPolicyConfig'>
        LeRobotPolicyModule = <class 'dimos.learning.lerobot_policy.LeRobotPolicyModule'>
        OnExisting = <enum 'OnExisting'>
        Path       = <class 'pathlib.Path'>
        TaskConfig = <class 'dimos.control.coordinator.TaskConfig'>
        Transform  = <class 'dimos.msgs.geometry_msgs.Transform.Transform'>
        Webcam     = <class 'dimos.hardware.sensors.camera.webcam.Webcam'>
        __builtins__ = <builtins>
        __cached__ = '.../blueprints/__pycache__/basic.cpython-312.pyc'
        __doc__    = 'Basic Galaxea A1Z coordinator blueprint.'
        __file__   = '.../work/dimos/dimos/.../galaxea_a1z/blueprints/basic.py'
        __loader__ = <_frozen_importlib_external.SourceFileLoader object at 0xff854f097620>
        __name__   = 'dimos.robot.manipulators.galaxea_a1z.blueprints.basic'
        __package__ = 'dimos.robot.manipulators.galaxea_a1z.blueprints'
        __spec__   = ModuleSpec(name='dimos.robot.manipulators.galaxea_a1z.blueprints.basic', loader=<_frozen_importlib_external.SourceFile...ct at 0xff854f097620>, origin='.../work/dimos/dimos/.../galaxea_a1z/blueprints/basic.py')
        annotations = _Feature((3, 7, 0, 'beta', 1), None, 16777216)
        autoconnect = <function autoconnect at 0xff862efd1620>
        galaxea_a1z_hardware = <function galaxea_a1z_hardware at 0xff854e8db6a0>
        partial    = <class 'functools.partial'>
dimos/utils/data.py:369: in __str__
    return str(self._ensure_downloaded())
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempt...a/.lfs/a1z_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff854f0a5ad0>
dimos/utils/data.py:347: in _ensure_downloaded
    cache = get_data(filename)
        cache      = None
        filename   = '.../A1Z_G1Z/urdf/A1Z_G1Z.urdf'
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempt...a/.lfs/a1z_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff854f0a5ad0>
dimos/utils/data.py:304: in get_data
    archive_path = _decompress_archive(_pull_lfs_archive(archive_name))
        archive_name = 'a1z_description'
        data_dir   = PosixPath('.../dimos/dimos/data')
        file_path  = PosixPath('.../dimos/dimos/data/.../A1Z_G1Z/urdf/A1Z_G1Z.urdf')
        name       = '.../A1Z_G1Z/urdf/A1Z_G1Z.urdf'
        nested_path = PosixPath('A1Z_G1Z/urdf/A1Z_G1Z.urdf')
        path_parts = ('a1z_description', 'A1Z_G1Z', 'urdf', 'A1Z_G1Z.urdf')
dimos/utils/data.py:248: in _pull_lfs_archive
    _lfs_pull(file_path, repo_root)
        file_path  = PosixPath('.../dimos/data/.lfs/a1z_description.tar.gz')
        filename   = 'a1z_description'
        repo_root  = PosixPath('.../work/dimos/dimos')
dimos/utils/data.py:216: in _lfs_pull
    raise RuntimeError(
E   RuntimeError: Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempts: Command '['git', 'lfs', 'pull', '--include', 'data/.lfs/a1z_description.tar.gz']' returned non-zero exit status 1.
        attempt    = 3
        env        = {'ACCEPT_EULA': 'Y', 'ACTIONS_ID_TOKEN_REQUEST_TOKEN': 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjM4ODI2YjE3LTZhMzAtNWY5Yi1iMTY5LT...-version=2.0', 'ACTIONS_ORCHESTRATION_ID': '3286e859-7370-437d-b243-50fc10048376.tests.ubuntu-24_04-arm_3_14_fal', ...}
        file_path  = PosixPath('.../dimos/data/.lfs/a1z_description.tar.gz')
        last_err   = CalledProcessError(1, ['git', 'lfs', 'pull', '--include', 'data/.lfs/a1z_description.tar.gz'])
        relative_path = PosixPath('data/.lfs/a1z_description.tar.gz')
        repo_root  = PosixPath('.../work/dimos/dimos')
        retries    = 2
dimos.codebase_checks.test_no_sections::test_no_section_markers
Stack Traces | 0.625s run time
def test_no_section_markers():
        """
        Fail if any file contains section-style comment markers.
    
        If a file is too complicated to be understood without sections, then the
        sections should be files. We don't need "subfiles".
        """
        violations = find_section_markers()
        if violations:
            report_lines = [
                f"Found {len(violations)} section marker(s). "
                "If a file is too complicated to be understood without sections, "
                'then the sections should be files. We don\'t need "subfiles".',
                "",
            ]
            for path, lineno, text in violations:
                report_lines.append(f"  {path}:{lineno}: {text.strip()}")
>           raise AssertionError("\n".join(report_lines))
E           AssertionError: Found 2 section marker(s). If a file is too complicated to be understood without sections, then the sections should be files. We don't need "subfiles".
E           
E             .../manipulators/galaxea_a1z/adapter.py:607: # --- A1Z-specific extensions (beyond ManipulatorAdapter protocol) ---
E             .../manipulators/galaxea_a1z/adapter.py:620: # --- internals ---

lineno     = 620
path       = '.../manipulators/galaxea_a1z/adapter.py'
report_lines = ['Found 2 section marker(s). If a file is too complicated to be understood without sections, then the sections should ...ond ManipulatorAdapter protocol) ---', '  .../manipulators/galaxea_a1z/adapter.py:620: # --- internals ---']
text       = '    # --- internals ---'
violations = [('.../manipulators/galaxea_a1z/adapter.py', 607, '    # --- A1Z-specific extensions (beyond ManipulatorAdapter protocol) ---'), ('.../manipulators/galaxea_a1z/adapter.py', 620, '    # --- internals ---')]

dimos/codebase_checks/test_no_sections.py:145: AssertionError
dimos.robot.test_all_blueprints::test_blueprint_is_valid[coordinator-galaxea-a1z]
Stack Traces | 3.15s run time
blueprint_name = 'coordinator-galaxea-a1z'

    @pytest.mark.parametrize("blueprint_name", UBUNTU_BLUEPRINTS)
    def test_blueprint_is_valid(blueprint_name: str) -> None:
        """Validate blueprints that should import on the ubuntu-latest runner."""
>       _check_blueprint(blueprint_name)

blueprint_name = 'coordinator-galaxea-a1z'

dimos/robot/test_all_blueprints.py:108: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/robot/test_all_blueprints.py:84: in _check_blueprint
    blueprint = get_blueprint_by_name(blueprint_name)
        blueprint_name = 'coordinator-galaxea-a1z'
        message    = "Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempts: Command '['git', 'lfs', 'pull', '--include', 'data/.lfs/a1z_description.tar.gz']' returned non-zero exit status 1."
dimos/robot/get_all_blueprints.py:51: in get_blueprint_by_name
    module = __import__(module_path, fromlist=[attr])
        attr       = 'coordinator_galaxea_a1z'
        module_path = 'dimos.robot.manipulators.galaxea_a1z.blueprints.basic'
        name       = 'coordinator-galaxea-a1z'
.../galaxea_a1z/blueprints/basic.py:43: in <module>
    _A1Z_DYNAMICS_URDF = str(A1Z_G1Z_MODEL_PATH)
        A1Z_G1Z_MODEL_PATH = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempt...a/.lfs/a1z_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff1cbfbe76d0>
        A1Z_POLICY_TASK_NAME = 'lerobot_servo_arm'
        A1Z_REPLAY_TASK_NAME = 'teach_replay_arm'
        A1Z_TEACH_CAMERA_FPS = 15.0
        A1Z_TEACH_CAMERA_HEIGHT = 480
        A1Z_TEACH_CAMERA_WIDTH = 640
        Blueprint  = <class 'dimos.core.coordination.blueprints.Blueprint'>
        CameraModule = <class 'dimos.hardware.sensors.camera.module.CameraModule'>
        CollectionRecorder = <class 'dimos.learning.collection.recorder.CollectionRecorder'>
        ControlCoordinator = <class 'dimos.control.coordinator.ControlCoordinator'>
        EpisodeMonitorModule = <class 'dimos.learning.collection.episode_monitor.EpisodeMonitorModule'>
        LeRobotPolicyConfig = <class 'dimos.learning.lerobot_policy.LeRobotPolicyConfig'>
        LeRobotPolicyModule = <class 'dimos.learning.lerobot_policy.LeRobotPolicyModule'>
        OnExisting = <enum 'OnExisting'>
        Path       = <class 'pathlib.Path'>
        TaskConfig = <class 'dimos.control.coordinator.TaskConfig'>
        Transform  = <class 'dimos.msgs.geometry_msgs.Transform.Transform'>
        Webcam     = <class 'dimos.hardware.sensors.camera.webcam.Webcam'>
        __builtins__ = <builtins>
        __cached__ = '.../blueprints/__pycache__/basic.cpython-312.pyc'
        __doc__    = 'Basic Galaxea A1Z coordinator blueprint.'
        __file__   = '.../work/dimos/dimos/.../galaxea_a1z/blueprints/basic.py'
        __loader__ = <_frozen_importlib_external.SourceFileLoader object at 0xff1cc182c0b0>
        __name__   = 'dimos.robot.manipulators.galaxea_a1z.blueprints.basic'
        __package__ = 'dimos.robot.manipulators.galaxea_a1z.blueprints'
        __spec__   = ModuleSpec(name='dimos.robot.manipulators.galaxea_a1z.blueprints.basic', loader=<_frozen_importlib_external.SourceFile...ct at 0xff1cc182c0b0>, origin='.../work/dimos/dimos/.../galaxea_a1z/blueprints/basic.py')
        annotations = _Feature((3, 7, 0, 'beta', 1), None, 16777216)
        autoconnect = <function autoconnect at 0xff1da04c1620>
        galaxea_a1z_hardware = <function galaxea_a1z_hardware at 0xff1cbfb78540>
        partial    = <class 'functools.partial'>
dimos/utils/data.py:369: in __str__
    return str(self._ensure_downloaded())
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempt...a/.lfs/a1z_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff1cbfbe76d0>
dimos/utils/data.py:347: in _ensure_downloaded
    cache = get_data(filename)
        cache      = None
        filename   = '.../A1Z_G1Z/urdf/A1Z_G1Z.urdf'
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempt...a/.lfs/a1z_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff1cbfbe76d0>
dimos/utils/data.py:304: in get_data
    archive_path = _decompress_archive(_pull_lfs_archive(archive_name))
        archive_name = 'a1z_description'
        data_dir   = PosixPath('.../dimos/dimos/data')
        file_path  = PosixPath('.../dimos/dimos/data/.../A1Z_G1Z/urdf/A1Z_G1Z.urdf')
        name       = '.../A1Z_G1Z/urdf/A1Z_G1Z.urdf'
        nested_path = PosixPath('A1Z_G1Z/urdf/A1Z_G1Z.urdf')
        path_parts = ('a1z_description', 'A1Z_G1Z', 'urdf', 'A1Z_G1Z.urdf')
dimos/utils/data.py:248: in _pull_lfs_archive
    _lfs_pull(file_path, repo_root)
        file_path  = PosixPath('.../dimos/data/.lfs/a1z_description.tar.gz')
        filename   = 'a1z_description'
        repo_root  = PosixPath('.../work/dimos/dimos')
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

file_path = PosixPath('.../dimos/data/.lfs/a1z_description.tar.gz')
repo_root = PosixPath('.../work/dimos/dimos')

    def _lfs_pull(file_path: Path, repo_root: Path, *, retries: int = 2) -> None:
        relative_path = file_path.relative_to(repo_root)
    
        env = os.environ.copy()
        env["GIT_LFS_FORCE_PROGRESS"] = "1"
    
        last_err: subprocess.CalledProcessError | None = None
        for attempt in range(1, retries + 2):  # retries + 1 total attempts
            try:
                subprocess.run(
                    ["git", "lfs", "pull", "--include", str(relative_path)],
                    cwd=repo_root,
                    check=True,
                    env=env,
                )
                return
            except subprocess.CalledProcessError as e:
                last_err = e
                if attempt <= retries:
                    time.sleep(attempt)  # 1s, 2s backoff
    
>       raise RuntimeError(
            f"Failed to pull LFS file {file_path} after {retries + 1} attempts: {last_err}"
        )
E       RuntimeError: Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempts: Command '['git', 'lfs', 'pull', '--include', 'data/.lfs/a1z_description.tar.gz']' returned non-zero exit status 1.

attempt    = 3
env        = {'ACCEPT_EULA': 'Y', 'ACTIONS_ID_TOKEN_REQUEST_TOKEN': 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjM4ODI2YjE3LTZhMzAtNWY5Yi1iMTY5LT...-version=2.0', 'ACTIONS_ORCHESTRATION_ID': '3286e859-7370-437d-b243-50fc10048376.tests.ubuntu-24_04-arm_3_14_fal', ...}
file_path  = PosixPath('.../dimos/data/.lfs/a1z_description.tar.gz')
last_err   = CalledProcessError(1, ['git', 'lfs', 'pull', '--include', 'data/.lfs/a1z_description.tar.gz'])
relative_path = PosixPath('data/.lfs/a1z_description.tar.gz')
repo_root  = PosixPath('.../work/dimos/dimos')
retries    = 2

dimos/utils/data.py:216: RuntimeError
dimos.codebase_checks.test_blueprint_kwargs::test_blueprint_atom_kwargs_match_module_config[coordinator-galaxea-a1z]
Stack Traces | 3.15s run time
blueprint_name = 'coordinator-galaxea-a1z'

    @pytest.mark.parametrize("blueprint_name", _blueprint_params())
    def test_blueprint_atom_kwargs_match_module_config(blueprint_name: str) -> None:
        """Fail when blueprint kwargs cannot be consumed by their target module."""
>       blueprint = _get_blueprint_or_skip(blueprint_name)

blueprint_name = 'coordinator-galaxea-a1z'

dimos/codebase_checks/test_blueprint_kwargs.py:91: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/codebase_checks/test_blueprint_kwargs.py:36: in _get_blueprint_or_skip
    return get_blueprint_by_name(blueprint_name)
        blueprint_name = 'coordinator-galaxea-a1z'
        message    = "Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempts: Command '['git', 'lfs', 'pull', '--include', 'data/.lfs/a1z_description.tar.gz']' returned non-zero exit status 1."
dimos/robot/get_all_blueprints.py:51: in get_blueprint_by_name
    module = __import__(module_path, fromlist=[attr])
        attr       = 'coordinator_galaxea_a1z'
        module_path = 'dimos.robot.manipulators.galaxea_a1z.blueprints.basic'
        name       = 'coordinator-galaxea-a1z'
.../galaxea_a1z/blueprints/basic.py:43: in <module>
    _A1Z_DYNAMICS_URDF = str(A1Z_G1Z_MODEL_PATH)
        A1Z_G1Z_MODEL_PATH = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempt...a/.lfs/a1z_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff854f0a5ad0>
        A1Z_POLICY_TASK_NAME = 'lerobot_servo_arm'
        A1Z_REPLAY_TASK_NAME = 'teach_replay_arm'
        A1Z_TEACH_CAMERA_FPS = 15.0
        A1Z_TEACH_CAMERA_HEIGHT = 480
        A1Z_TEACH_CAMERA_WIDTH = 640
        Blueprint  = <class 'dimos.core.coordination.blueprints.Blueprint'>
        CameraModule = <class 'dimos.hardware.sensors.camera.module.CameraModule'>
        CollectionRecorder = <class 'dimos.learning.collection.recorder.CollectionRecorder'>
        ControlCoordinator = <class 'dimos.control.coordinator.ControlCoordinator'>
        EpisodeMonitorModule = <class 'dimos.learning.collection.episode_monitor.EpisodeMonitorModule'>
        LeRobotPolicyConfig = <class 'dimos.learning.lerobot_policy.LeRobotPolicyConfig'>
        LeRobotPolicyModule = <class 'dimos.learning.lerobot_policy.LeRobotPolicyModule'>
        OnExisting = <enum 'OnExisting'>
        Path       = <class 'pathlib.Path'>
        TaskConfig = <class 'dimos.control.coordinator.TaskConfig'>
        Transform  = <class 'dimos.msgs.geometry_msgs.Transform.Transform'>
        Webcam     = <class 'dimos.hardware.sensors.camera.webcam.Webcam'>
        __builtins__ = <builtins>
        __cached__ = '.../blueprints/__pycache__/basic.cpython-312.pyc'
        __doc__    = 'Basic Galaxea A1Z coordinator blueprint.'
        __file__   = '.../work/dimos/dimos/.../galaxea_a1z/blueprints/basic.py'
        __loader__ = <_frozen_importlib_external.SourceFileLoader object at 0xff85510de780>
        __name__   = 'dimos.robot.manipulators.galaxea_a1z.blueprints.basic'
        __package__ = 'dimos.robot.manipulators.galaxea_a1z.blueprints'
        __spec__   = ModuleSpec(name='dimos.robot.manipulators.galaxea_a1z.blueprints.basic', loader=<_frozen_importlib_external.SourceFile...ct at 0xff85510de780>, origin='.../work/dimos/dimos/.../galaxea_a1z/blueprints/basic.py')
        annotations = _Feature((3, 7, 0, 'beta', 1), None, 16777216)
        autoconnect = <function autoconnect at 0xff862efd1620>
        galaxea_a1z_hardware = <function galaxea_a1z_hardware at 0xff854e8db6a0>
        partial    = <class 'functools.partial'>
dimos/utils/data.py:369: in __str__
    return str(self._ensure_downloaded())
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempt...a/.lfs/a1z_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff854f0a5ad0>
dimos/utils/data.py:347: in _ensure_downloaded
    cache = get_data(filename)
        cache      = None
        filename   = '.../A1Z_G1Z/urdf/A1Z_G1Z.urdf'
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempt...a/.lfs/a1z_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff854f0a5ad0>
dimos/utils/data.py:304: in get_data
    archive_path = _decompress_archive(_pull_lfs_archive(archive_name))
        archive_name = 'a1z_description'
        data_dir   = PosixPath('.../dimos/dimos/data')
        file_path  = PosixPath('.../dimos/dimos/data/.../A1Z_G1Z/urdf/A1Z_G1Z.urdf')
        name       = '.../A1Z_G1Z/urdf/A1Z_G1Z.urdf'
        nested_path = PosixPath('A1Z_G1Z/urdf/A1Z_G1Z.urdf')
        path_parts = ('a1z_description', 'A1Z_G1Z', 'urdf', 'A1Z_G1Z.urdf')
dimos/utils/data.py:248: in _pull_lfs_archive
    _lfs_pull(file_path, repo_root)
        file_path  = PosixPath('.../dimos/data/.lfs/a1z_description.tar.gz')
        filename   = 'a1z_description'
        repo_root  = PosixPath('.../work/dimos/dimos')
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

file_path = PosixPath('.../dimos/data/.lfs/a1z_description.tar.gz')
repo_root = PosixPath('.../work/dimos/dimos')

    def _lfs_pull(file_path: Path, repo_root: Path, *, retries: int = 2) -> None:
        relative_path = file_path.relative_to(repo_root)
    
        env = os.environ.copy()
        env["GIT_LFS_FORCE_PROGRESS"] = "1"
    
        last_err: subprocess.CalledProcessError | None = None
        for attempt in range(1, retries + 2):  # retries + 1 total attempts
            try:
                subprocess.run(
                    ["git", "lfs", "pull", "--include", str(relative_path)],
                    cwd=repo_root,
                    check=True,
                    env=env,
                )
                return
            except subprocess.CalledProcessError as e:
                last_err = e
                if attempt <= retries:
                    time.sleep(attempt)  # 1s, 2s backoff
    
>       raise RuntimeError(
            f"Failed to pull LFS file {file_path} after {retries + 1} attempts: {last_err}"
        )
E       RuntimeError: Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempts: Command '['git', 'lfs', 'pull', '--include', 'data/.lfs/a1z_description.tar.gz']' returned non-zero exit status 1.

attempt    = 3
env        = {'ACCEPT_EULA': 'Y', 'ACTIONS_ID_TOKEN_REQUEST_TOKEN': 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjM4ODI2YjE3LTZhMzAtNWY5Yi1iMTY5LT...-version=2.0', 'ACTIONS_ORCHESTRATION_ID': '3286e859-7370-437d-b243-50fc10048376.tests.ubuntu-24_04-arm_3_14_fal', ...}
file_path  = PosixPath('.../dimos/data/.lfs/a1z_description.tar.gz')
last_err   = CalledProcessError(1, ['git', 'lfs', 'pull', '--include', 'data/.lfs/a1z_description.tar.gz'])
relative_path = PosixPath('data/.lfs/a1z_description.tar.gz')
repo_root  = PosixPath('.../work/dimos/dimos')
retries    = 2

dimos/utils/data.py:216: RuntimeError

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@Nabla7

Nabla7 commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator Author

@jeff-hykin can you explain a bit here the changes to core/zenoh that were made?

TomCC7 and others added 2 commits July 22, 2026 14:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants