Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
ef68c92
feat(messages): add installable built-in CDR definitions
TomCC7 Oct 21, 2026
deecf9a
feat(transport): support CDR alongside existing message encodings
TomCC7 Oct 21, 2026
086c166
feat(messages): install and register external CDR definitions
TomCC7 Oct 21, 2026
12c81e0
feat(recording): record raw CDR and read schema-aware MCAP
TomCC7 Oct 21, 2026
f574a2d
feat(viewers): render generated CDR messages in browser and Rerun
TomCC7 Oct 21, 2026
52d9cea
fix(viewers): use shared codecs and retain generated-message helpers
TomCC7 Oct 21, 2026
1e18dab
refactor(viewers): keep sensor adapters local and defer domain migration
TomCC7 Oct 21, 2026
5a917f6
test(viewers): demonstrate full bridge, MCAP playback and live browse…
TomCC7 Oct 21, 2026
4e77390
refactor(viewers): reuse native Rerun MCAP import for offline viewing
TomCC7 Oct 21, 2026
e020c9f
refactor(viewers): extract sensor utilities and isolate CDR test fixt…
TomCC7 Oct 21, 2026
71e2690
fix(viewers): use globalThis for browser lifecycle listener
TomCC7 Oct 21, 2026
22e03e1
fix(typing): resolve generated messages during editable type checks
TomCC7 Oct 21, 2026
95cecd4
fix(sdk): use the external CDR codec and shared message adapters
TomCC7 Oct 22, 2026
ebe68ca
fix(cdr): integrate corrected library ownership into PR #4366
TomCC7 Oct 22, 2026
0fb3547
fix(cdr): integrate corrected library ownership into PR #4732
TomCC7 Oct 22, 2026
aebb801
fix(cdr): integrate corrected library ownership into PR #4733
TomCC7 Oct 22, 2026
e194b2a
fix(cdr): integrate corrected library ownership into PR #4722
TomCC7 Oct 22, 2026
830c8f8
fix(cdr): integrate corrected library ownership into PR #4721
TomCC7 Oct 22, 2026
6eb6bf3
[autofix.ci] apply automated fixes
autofix-ci[bot] Oct 22, 2026
a5f3d1e
release(cdr): consume published cdrgen 0.1.2 in PR #4366
TomCC7 Oct 22, 2026
d4dbc82
release(cdr): consume published cdrgen 0.1.2 in PR #4774
TomCC7 Oct 22, 2026
35a1d87
release(cdr): consume published cdrgen 0.1.2 in PR #4732
TomCC7 Oct 22, 2026
1f4946c
release(cdr): consume published cdrgen 0.1.2 in PR #4733
TomCC7 Oct 22, 2026
a70abc3
release(cdr): consume published cdrgen 0.1.2 in PR #4722
TomCC7 Oct 22, 2026
6150634
release(cdr): consume published cdrgen 0.1.2 in PR #4721
TomCC7 Oct 22, 2026
1ca31e1
Use standard ROS TF conventions in CDR stack layer #4774
TomCC7 Oct 22, 2026
aaeca85
Use standard ROS TF conventions in CDR stack layer #4732
TomCC7 Oct 22, 2026
ab4d173
Use standard ROS TF conventions in CDR stack layer #4733
TomCC7 Oct 22, 2026
879c3d8
Use standard ROS TF conventions in CDR stack layer #4774
TomCC7 Oct 22, 2026
a712641
Use standard ROS TF conventions in CDR stack layer #4732
TomCC7 Oct 22, 2026
2921e83
Use standard ROS TF conventions in CDR stack layer #4733
TomCC7 Oct 22, 2026
a9abfc5
Use standard ROS TF conventions in CDR stack layer #4774
TomCC7 Oct 22, 2026
7390d29
Use standard ROS TF conventions in CDR stack layer #4732
TomCC7 Oct 22, 2026
25fba35
Use standard ROS TF conventions in CDR stack layer #4733
TomCC7 Oct 22, 2026
b31e792
Use standard ROS TF conventions in CDR stack layer #4774
TomCC7 Oct 22, 2026
a002034
Use standard ROS TF conventions in CDR stack layer #4732
TomCC7 Oct 22, 2026
4583a8d
Use standard ROS TF conventions in CDR stack layer #4733
TomCC7 Oct 22, 2026
7dacb4a
Integrate shared native CDR types into feature PR #4732
TomCC7 Oct 23, 2026
e5c0767
Integrate shared native CDR types into feature PR #4722
TomCC7 Oct 23, 2026
d1d8ce2
Integrate shared native CDR types into feature PR #4733
TomCC7 Oct 23, 2026
4511158
[autofix.ci] apply automated fixes
autofix-ci[bot] Oct 23, 2026
8f98604
Build shared native messages in feature PR #4722
TomCC7 Oct 23, 2026
e6eb717
Build shared native messages in feature PR #4732
TomCC7 Oct 23, 2026
2a88f94
Build shared native messages in feature PR #4733
TomCC7 Oct 23, 2026
5e0484b
refactor(messages): isolate image, calibration and time adapters
TomCC7 Oct 23, 2026
ec07ea0
refactor(messages): isolate generated geometry value operations
TomCC7 Oct 23, 2026
dada376
fix(pointcloud): preserve records without serialization copies
TomCC7 Oct 23, 2026
6587949
ci(cdr): run this adapter layer tests before SDK checks
TomCC7 Oct 23, 2026
623e138
Integrate the preceding reviewed adapter layer
TomCC7 Oct 23, 2026
f423112
ci(cdr): run this adapter layer tests before SDK checks
TomCC7 Oct 23, 2026
912279f
Integrate the preceding reviewed adapter layer
TomCC7 Oct 23, 2026
b136925
ci(cdr): run this adapter layer tests before SDK checks
TomCC7 Oct 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .github/scripts/test_relock_shared_flakes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright 2026 Dimensional Inc.
# SPDX-License-Identifier: Apache-2.0

"""Exact SDK API pins must survive automatic shared-source refreshes."""

from importlib.machinery import SourceFileLoader
from importlib.util import module_from_spec, spec_from_loader
from pathlib import Path
from tempfile import TemporaryDirectory
import unittest
from unittest.mock import patch

ROOT = Path(__file__).resolve().parents[2]
loader = SourceFileLoader("relock_shared_flakes", str(ROOT / "bin/relock-shared-flakes"))
spec = spec_from_loader(loader.name, loader)
relock = module_from_spec(spec)
loader.exec_module(relock)


class ExactSdkVersionTests(unittest.TestCase):
def stale(self, requirement: str, revision_matches: bool = True) -> list[str]:
revision = "a" * 40
manifest_revision = revision if revision_matches else "b" * 40
with TemporaryDirectory() as directory:
manifest = Path(directory) / "Cargo.toml"
manifest.write_text(
'[dependencies]\ndimos-module = { git = "'
+ relock.GIT_URL
+ '", rev = "'
+ manifest_revision
+ '"'
+ requirement
+ " }\n"
)
with (
patch.object(
relock,
"_pinned_crates",
return_value=[
("dimos-module", "0.1.0", revision),
("dimos-module-macros", "0.1.0", revision),
],
),
patch.object(
relock,
"_crate_dirs",
return_value={
"dimos-module": "native/rust/dimos-module",
"dimos-module-macros": "native/rust/dimos-module-macros",
},
),
patch.object(relock, "_git", return_value='[package]\nversion = "0.2.0"'),
patch.object(relock, "tree_at", side_effect=lambda rev, _: rev),
):
return relock._stale_pin(manifest, manifest.with_name("Cargo.lock"))

def test_exact_legacy_api_keeps_its_sdk_and_transitive_macro_revision(self):
self.assertEqual(self.stale(', version = "=0.1.0"'), [])

def test_unconstrained_dependencies_still_refresh_changed_sources(self):
self.assertEqual(len(self.stale("")), 2)

def test_exact_constraint_does_not_hide_a_mismatched_lock_revision(self):
failures = self.stale(', version = "=0.1.0"', revision_matches=False)
self.assertEqual(len(failures), 2)
self.assertTrue(all("Cargo.lock pins" in error for error in failures))


if __name__ == "__main__":
unittest.main()
4 changes: 3 additions & 1 deletion .github/workflows/autofix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ jobs:
run: bash docker/ros/install-nix.sh
- name: Relock the modules onto this revision
if: steps.shared.outputs.changed == 'true'
run: python3 bin/relock-shared-flakes
run: |
python3 .github/scripts/test_relock_shared_flakes.py
python3 bin/relock-shared-flakes

- name: autofix.ci
uses: autofix-ci/action@v1
47 changes: 47 additions & 0 deletions .github/workflows/cdr-native.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: CDR native interoperability

on:
pull_request:
workflow_dispatch:
push:
branches: [main]

permissions:
contents: read

jobs:
native:
runs-on: ubuntu-latest
timeout-minutes: 30
env:
DIMOS_ALLOW_MISSING_COCKPIT: "1"
CARGO_TARGET_DIR: ${{ github.workspace }}/target
steps:
- uses: actions/checkout@v7
with:
lfs: false
persist-credentials: false
- uses: astral-sh/setup-uv@v10.2.0
with:
python-version: '3.12'
enable-cache: true
- name: Install build and media dependencies
run: |
sudo apt-get update
sudo apt-get install -y cmake pkg-config clang libclang-dev portaudio19-dev libturbojpeg ffmpeg
- name: Install Python project and test dependencies
run: uv sync --frozen --group tests
- name: Test generated value adapters
run: |
uv run --no-sync pytest --noconftest --import-mode=importlib -o addopts='' -q \
dimos/message_utils/test_image_views.py \
dimos/message_utils/test_image_compression.py \
dimos/message_utils/test_camera_info.py \
dimos/message_utils/test_time.py \
dimos/message_utils/test_geometry.py \
dimos/message_utils/test_pointcloud.py \
dimos/message_utils/test_pointcloud_colors.py
- name: Test native SDK
run: |
cargo test --locked --manifest-path native/rust/dimos-module/Cargo.toml
cargo test --locked --manifest-path native/rust/dimos-module-macros/Cargo.toml
41 changes: 0 additions & 41 deletions .github/workflows/message-codegen.yml

This file was deleted.

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,6 @@ optuna.db

# policy blobs are loaded from outside the repository
*.bin

# Prepared from schema-sources.json; keep downloaded schemas out of Git.
/packages/dimos-generated/interfaces/
5 changes: 5 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,8 @@ prune dimos/web/command-center-extension

global-exclude test_*.py
global-exclude conftest.py

# Built-in message inputs are prepared automatically during source builds; the sdist is self-contained.
include packages/dimos-generated/schema-sources.json
recursive-include packages/dimos-generated/custom *.msg LICENSE
recursive-include packages/dimos-generated/interfaces *.msg *LICENSE*
17 changes: 15 additions & 2 deletions bin/relock-shared-flakes
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ rewrites a pull request's merge ref, so a pin on it would rot.
What counts as stale is the pinned *content*, not the revision. A pin may name an
older commit quite legitimately -- what matters is that the shared tree at that
commit is the tree in this one.
An explicit exact Cargo version keeps its pinned revision, including transitive
SDK crates. Updating that API is a caller migration rather than an automatic relock.

Stdlib only, so it runs on a bare `python3` like the other bin/ scripts.
"""
Expand Down Expand Up @@ -296,10 +298,19 @@ def _hashes_file(manifest: Path) -> Path:
def _stale_pin(manifest: Path, lock: Path) -> list[str]:
"""Why the pin on this repository's crates is not this commit's; empty when it is."""
revs = {m.group("rev") for m in _GIT_DEP.finditer(manifest.read_text())}
dependencies = tomllib.loads(manifest.read_text()).get("dependencies", {})
packages = _pinned_crates(lock)
fixed_revs = set()
for name, version, rev in packages:
dependency = dependencies.get(name, {})
if isinstance(dependency, dict) and dependency.get("version") == f"={version}":
fixed_revs.add(rev)
stale = []
for name, _, rev in _pinned_crates(lock):
for name, _, rev in packages:
if rev not in revs:
stale.append(f"{name}: Cargo.lock pins {rev[:10]}, Cargo.toml another revision")
if rev in fixed_revs:
continue
subdir = _crate_dirs().get(name)
pinned = tree_at(rev, subdir) if subdir else None
if subdir is None or (pinned is not None and pinned != tree_at("HEAD", subdir)):
Expand Down Expand Up @@ -345,7 +356,9 @@ def relock_cargo(manifest: Path) -> tuple[Path, bool, list[str]]:
return manifest, False, [_unpushed(manifest, " and ".join(subdirs))]
manifest.write_text(
_GIT_DEP.sub(
lambda m: f'{m.group("name")} = {{ git = "{GIT_URL}", rev = "{rev}"{m.group("rest")}}}',
lambda m: (
f'{m.group("name")} = {{ git = "{GIT_URL}", rev = "{rev}"{m.group("rest")}}}'
),
before[manifest],
)
)
Expand Down
8 changes: 4 additions & 4 deletions dimos/core/coordination/test_blueprints.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
from dimos.core.core import rpc
from dimos.core.module import Module
from dimos.core.stream import In, Out
from dimos.core.transport import LCMTransport
from dimos.core.transport import pLCMTransport
from dimos.spec.utils import Spec


Expand Down Expand Up @@ -139,7 +139,7 @@ def test_config_rejects_unknown_fields_and_sections() -> None:


def test_transports() -> None:
custom_transport = LCMTransport("/custom_topic", Data1)
custom_transport = pLCMTransport("/custom_topic")
blueprint_set = autoconnect(ModuleA.blueprint(), ModuleB.blueprint()).transports(
{("data1", Data1): custom_transport}
)
Expand Down Expand Up @@ -414,12 +414,12 @@ def test_explicit_instance_name_sets_blueprint_identity() -> None:
def test_namespace_prefixes_pinned_transports() -> None:
blueprint = (
autoconnect(ModuleA.blueprint(), ModuleB.blueprint())
.transports({("data1", Data1): LCMTransport("/custom_topic", Data1)})
.transports({("data1", Data1): pLCMTransport("/custom_topic")})
.namespace("robot0")
)

transport = blueprint.transport_map[("robot0/data1", Data1)]
assert transport.topic.pattern == "/robot0/custom_topic"
assert transport.topic == "/robot0/custom_topic"
assert ("data1", Data1) not in blueprint.transport_map


Expand Down
1 change: 1 addition & 0 deletions dimos/core/global_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class GlobalConfig(BaseSettings):
replay_db: str = "go2_short"
record: Literal["", "sqlite", "mcap"] = ""
record_engine: Literal["python", "rust"] = Field(default="python", validate_default=True)
record_executable: str | None = None # Optional prebuilt Rust recorder path.
record_topics: str = "*" # comma-separated globs on the topic slug (/a/b -> a_b)
record_encoding_threads: int | None = Field(default=None, ge=1)
# How every zenoh session this process opens joins the network.
Expand Down
37 changes: 37 additions & 0 deletions dimos/core/test_dds_transport.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Copyright 2026 Dimensional Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from dataclasses import dataclass

import pytest

from dimos.core.transport import DDSTransport

idl = pytest.importorskip("cyclonedds.idl")


@dataclass
class NativeSample(idl.IdlStruct):
value: int


def test_dds_transport_accepts_native_idl_type():
transport = DDSTransport("native_sample", NativeSample)
try:
assert transport.topic.name == "native_sample"
assert transport.topic.data_type is NativeSample
sample = NativeSample(value=42)
assert transport.topic.data_type.deserialize(sample.serialize()) == sample
finally:
transport.stop()
39 changes: 37 additions & 2 deletions dimos/core/test_transport_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@

from dimos.core.global_config import GlobalConfig
from dimos.core.transport import (
DDSTransport,
LCMTransport,
ROSTransport,
ZenohTransport,
pLCMTransport,
pZenohTransport,
Expand All @@ -31,6 +33,8 @@
session_config,
transport_topic,
)
from dimos.messages.sensor_msgs.msg import Image as CdrImage
from dimos.messages.std_msgs.msg import Int32
from dimos.msgs.geometry_msgs.PoseStamped import PoseStamped
from dimos.msgs.geometry_msgs.Twist import Twist
from dimos.msgs.sensor_msgs.Image import Image
Expand Down Expand Up @@ -82,8 +86,9 @@ def test_make_transport_zenoh_pickled() -> None:
assert t.topic == "dimos/human_input"


def test_default_zenoh_qos_high_rate_sensor_types_drop() -> None:
assert default_zenoh_qos("/camera/color", Image) == QOS_LATEST_WINS
@pytest.mark.parametrize("message_type", [Image, CdrImage])
def test_default_zenoh_qos_high_rate_sensor_types_drop(message_type) -> None:
assert default_zenoh_qos("/camera/color", message_type) == QOS_LATEST_WINS


def test_default_zenoh_qos_agent_channels_never_drop() -> None:
Expand Down Expand Up @@ -155,3 +160,33 @@ def test_apply_transport_arg_bad_value_exits(argv: list[str]) -> None:
apply_transport_arg(argv, g=g)
assert exc.value.code == 2
assert g.transport == "lcm" # config left untouched


@pytest.mark.parametrize("config", [LCM, ZENOH])
def test_factory_binds_generated_messages(config):
transport = make_transport("generated", Int32, g=config)
assert transport.topic.message.encoding == "cdr"
assert transport.topic.message.message_type is Int32


@pytest.mark.parametrize("config", [LCM, ZENOH])
@pytest.mark.parametrize("message_type", [dict, int, str, bool])
def test_factory_preserves_pickle_for_python_types(config, message_type):
transport = make_transport("python", message_type, g=config)
expected = pLCMTransport if config.transport == "lcm" else pZenohTransport
assert isinstance(transport, expected)


@pytest.mark.parametrize("transport_type", [ROSTransport, DDSTransport])
def test_generated_messages_require_ros_dds_type_adapters(transport_type):
with pytest.raises(TypeError, match="type adapter"):
transport_type("generated", Int32)


@pytest.mark.parametrize("config", [LCM, ZENOH])
def test_factory_does_not_pickle_unregistered_cdr_types(config):
class Unregistered:
__msgtype__ = "test_msgs/msg/Unregistered"

with pytest.raises(TypeError):
make_transport("invalid", Unregistered, g=config)
Loading
Loading