Skip to content

feat: cut typed runtime and browser consumers over to CDR (5/7) - #4363

Closed
TomCC7 wants to merge 11 commits into
feat/cdr-candidate-authoringfrom
feat/cdr-candidate-runtime
Closed

TomCC7 wants to merge 11 commits into
feat/cdr-candidate-authoringfrom
feat/cdr-candidate-runtime

Conversation

@TomCC7

@TomCC7 TomCC7 commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Typed runtime, native SDKs, robot/perception/navigation consumers and browser channels use generated CDR values. Existing recorder/readers/replay remain usable at this boundary: CDR/SQLite codecs, full native MCAP schemas, type/timestamp handling and selected TF ports are prerequisites here. The self-contained three-language coordinator guide and example run against the exported SDK. Integrated recording acceptance follows separately; unused old classes remain until retirement.

Base: #4362. This is layer 5/7, replacing the combined draft #4257 through the user-approved split. Original PR1–3 and the old feat/cdr-runtime-cutover branch/history are preserved. Reassembly uses selected files/hunks and forward merges, not a claim that the original commits cleanly cherry-pick.

Published HEAD: b40967da0704b8c62a72e56666ec04e06df093f9.

Validation: 122 focused tests passed; two unchanged default-host LCM cases failed at handler startup because multicast is unavailable. Offline selection: 118 passed, 6 deselected. Candidate C++ SDK compiled/installed; 2 CDR cases/8 assertions passed; exact Python→C++→Rust coordinator passed 20.5→21.5→22.5→23.5 with Header/sequence preserved over loopback Zenoh. Hooks passed. GitHub CI is pending.

This remains a proposal with a deliberate old-message API/wire break. CDR replaces encoding; LCM/Zenoh remain transports. No old-recording fallback is promised. No merge or package registry release is requested. External gates remain M20 SDK, host multicast, Foxglove sign-in and human/full-pose visual acceptance. Benchmarks have not been waived or weakened.

Published replacement chain: #4247 → #4252 → #4254 → #4362 → #4363 → #4364 → #4365. Preserved full reference: #4257 at cb982817bde248baa70a6fa69cc9ecc275c43707.

Tutorial follow-up: the checkout workflow is documented in docs/development/messages-in-repository.md; the installed-runtime/custom-project workflow is in docs/development/messages-external-project.md (runtime layer onward); both share message-reference.md. Matching review wheels are required; pip does not resolve uv-only local sources. Clean isolated canonical .msg addition→uv build→wheel install→value/codec checks passed in 112 seconds without installing the DimOS runtime. Existing isolated external source/wheel/sdist/editable acceptance and exact three-language coordinator evidence are retained; embedded C++ includes are now checked correctly. Required toolchains, SDK artifacts and harness acquisition are explicit.

Initial authoring CI exposed a premature root Rust workspace join (lock/Nix source mismatch) and a cold docs build missing Fast CDR. The generated crate remains standalone in authoring and joins the root workspace at runtime cutover; the existing Fast CDR setup now precedes package installation. Locks/assertions/gates were not relaxed. New exact-HEAD CI remains pending.

Authoring namespace test runner now invokes python -m pytest so a clean checkout imports the lightweight message namespace; 11 focused project tests passed. Self-hosted setup now explicitly installs CMake before building Fast CDR, and rosbags==0.11.0 and mcap>=1.2.0 are declared in the shared test group with its locked dependency; eight focused ownership/MCAP cases and hooks passed. Exact updated-head checks remain pending.

Superseded by naming-only replacement #4369. Exact commit b40967da0704b8c62a72e56666ec04e06df093f9, source tree and parent-relative diff are unchanged.

Verified native seven-layer stack #4372: #4247 → #4366 → #4367 → #4368 → #4369 → #4370 → #4371. Stack: #4247

All branches use cc/ and the ordered PR bases preserve the prior commit ancestry. Old branches/history are retained. Replacement PR checks are pending and prior CI retains its exact SHA/run/ref provenance. No merge, force push or release.

@codecov

codecov Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

❌ 4 Tests Failed:

Tests completed Failed Passed Skipped
6365 4 6361 96
View the top 3 failed test(s) by shortest run time
::dimos.navigation.nav_3d.mls_planner.utils.test_plan_rrd
Stack Traces | 0s run time
ImportError while importing test module '.../mls_planner/utils/test_plan_rrd.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
.../mapping/ray_tracing/voxel_map.py:20: in <module>
    from dimos_voxel_ray_tracing import VoxelRayMapper  # noqa: F401  (re-exported)
E   ModuleNotFoundError: No module named 'dimos_voxel_ray_tracing'

The above exception was the direct cause of the following exception:
.../mls_planner/utils/test_plan_rrd.py:24: in <module>
    from dimos.navigation.nav_3d.mls_planner.utils.plan_rrd import _log_odometry, _TfSync
.../mls_planner/utils/plan_rrd.py:34: in <module>
    from dimos.mapping.ray_tracing.transformer import RayTraceMap
.../mapping/ray_tracing/transformer.py:23: in <module>
    from dimos.mapping.ray_tracing.voxel_map import VoxelRayMapper
.../mapping/ray_tracing/voxel_map.py:22: in <module>
    raise ImportError(
E   ImportError: dimos_voxel_ray_tracing is not built. Run: uv run maturin develop --release --uv -m .../rust/py/Cargo.toml
dimos.mapping.dim_slam.test_dim_odom_replay::test_dim_odom_fusion_replay
Stack Traces | 0.021s run time
def test_dim_odom_fusion_replay():
        """IMU + wheel odometry through OdometryFusion, twice, identically."""
        db_path = str(get_data(FUSION_SNIPPET))
>       first = _replay_fusion(db_path)

db_path    = '.../dimos/data/alfred_fusion_short.db'

.../mapping/dim_slam/test_dim_odom_replay.py:213: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../mapping/dim_slam/test_dim_odom_replay.py:147: in _replay_fusion
    base_from_imu = _tf_lookup(store)("base_link", IMU_FRAME)
        db_path    = '.../dimos/data/alfred_fusion_short.db'
        store      = <dimos.memory.store.sqlite.SqliteStore object at 0x794df554ad20>
.../mapping/dim_slam/test_dim_odom_replay.py:73: in _tf_lookup
    tf = StreamTF.from_store(store)
        store      = <dimos.memory.store.sqlite.SqliteStore object at 0x794df554ad20>
dimos/memory/tf.py:60: in from_store
    recorded = tf_stream(store, stream)
        cls        = <class 'dimos.memory.tf.StreamTF'>
        store      = <dimos.memory.store.sqlite.SqliteStore object at 0x794df554ad20>
        stream     = 'tf'
dimos/memory/tf.py:38: in tf_stream
    return cast("Stream[TFMessage]", store.stream(stream, TFMessage))
        store      = <dimos.memory.store.sqlite.SqliteStore object at 0x794df554ad20>
        stream     = 'tf'
.../memory/store/base.py:197: in stream
    backend = self._create_backend(name, payload_type, **resolved)
        name       = 'tf'
        overrides  = {}
        payload_type = <class 'dimos_generated.tf2_msgs.msg.TFMessage'>
        resolved   = {'eager_blobs': False, 'must_exist': True, 'page_size': 256, 'path': '.../dimos/data/alfred_fusion_short.db'}
        self       = <dimos.memory.store.sqlite.SqliteStore object at 0x794df554ad20>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <dimos.memory.store.sqlite.SqliteStore object at 0x794df554ad20>
name = 'tf', payload_type = <class 'dimos_generated.tf2_msgs.msg.TFMessage'>
config = {'eager_blobs': False, 'must_exist': True, 'page_size': 256, 'path': '.../dimos/data/alfred_fusion_short.db'}
stored = {'blob_store': {'class': 'dimos.memory.blobstore.sqlite.SqliteBlobStore', 'config': {'path': None}}, 'codec_id': 'lcm', 'eager_blobs': False, 'notifier': {'class': 'dimos.memory.notifier.subject.SubjectNotifier', 'config': {}}, ...}
actual_module = 'dimos_generated.tf2_msgs.msg.TFMessage'

    def _create_backend(
        self, name: str, payload_type: type[Any] | None = None, **config: Any
    ) -> Backend[Any]:
        validate_identifier(name)
    
        stored = self._registry.get(name)
    
        if stored is not None:
            # Load path: validate type, assemble from stored config
            if payload_type is not None:
                actual_module = f"{payload_type.__module__}.{payload_type.__qualname__}"
                if actual_module != stored["payload_module"]:
>                   raise ValueError(
                        f"Stream {name!r} was created with type {stored['payload_module']}, "
                        f"but opened with {actual_module}"
                    )
E                   ValueError: Stream 'tf' was created with type dimos.msgs.tf2_msgs.TFMessage.TFMessage, but opened with dimos_generated.tf2_msgs.msg.TFMessage

__class__  = <class 'dimos.memory.store.sqlite.SqliteStore'>
actual_module = 'dimos_generated.tf2_msgs.msg.TFMessage'
config     = {'eager_blobs': False, 'must_exist': True, 'page_size': 256, 'path': '.../dimos/data/alfred_fusion_short.db'}
name       = 'tf'
payload_type = <class 'dimos_generated.tf2_msgs.msg.TFMessage'>
self       = <dimos.memory.store.sqlite.SqliteStore object at 0x794df554ad20>
stored     = {'blob_store': {'class': 'dimos.memory.blobstore.sqlite.SqliteBlobStore', 'config': {'path': None}}, 'codec_id': 'lcm', 'eager_blobs': False, 'notifier': {'class': 'dimos.memory.notifier.subject.SubjectNotifier', 'config': {}}, ...}

.../memory/store/sqlite.py:161: ValueError
dimos.mapping.dim_slam.test_dim_odom_replay::test_dim_odom_stereo_replay
Stack Traces | 1.4s run time
def test_dim_odom_stereo_replay():
        """The same recording twice through dim_odom gives the same trajectory."""
        db_path = str(get_data(SNIPPET))
>       first = _replay_trajectory(db_path)

db_path    = '.../dimos/data/alfred_stereo_short.db'

.../mapping/dim_slam/test_dim_odom_replay.py:198: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../mapping/dim_slam/test_dim_odom_replay.py:110: in _replay_trajectory
    tf=_tf_lookup(store),
        db_path    = '.../dimos/data/alfred_stereo_short.db'
        store      = <dimos.memory.store.sqlite.SqliteStore object at 0x794df55496d0>
.../mapping/dim_slam/test_dim_odom_replay.py:73: in _tf_lookup
    tf = StreamTF.from_store(store)
        store      = <dimos.memory.store.sqlite.SqliteStore object at 0x794df55496d0>
dimos/memory/tf.py:60: in from_store
    recorded = tf_stream(store, stream)
        cls        = <class 'dimos.memory.tf.StreamTF'>
        store      = <dimos.memory.store.sqlite.SqliteStore object at 0x794df55496d0>
        stream     = 'tf'
dimos/memory/tf.py:38: in tf_stream
    return cast("Stream[TFMessage]", store.stream(stream, TFMessage))
        store      = <dimos.memory.store.sqlite.SqliteStore object at 0x794df55496d0>
        stream     = 'tf'
.../memory/store/base.py:197: in stream
    backend = self._create_backend(name, payload_type, **resolved)
        name       = 'tf'
        overrides  = {}
        payload_type = <class 'dimos_generated.tf2_msgs.msg.TFMessage'>
        resolved   = {'eager_blobs': False, 'must_exist': True, 'page_size': 256, 'path': '.../dimos/data/alfred_stereo_short.db'}
        self       = <dimos.memory.store.sqlite.SqliteStore object at 0x794df55496d0>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <dimos.memory.store.sqlite.SqliteStore object at 0x794df55496d0>
name = 'tf', payload_type = <class 'dimos_generated.tf2_msgs.msg.TFMessage'>
config = {'eager_blobs': False, 'must_exist': True, 'page_size': 256, 'path': '.../dimos/data/alfred_stereo_short.db'}
stored = {'blob_store': {'class': 'dimos.memory.blobstore.sqlite.SqliteBlobStore', 'config': {'path': None}}, 'codec_id': 'lcm', 'eager_blobs': False, 'notifier': {'class': 'dimos.memory.notifier.subject.SubjectNotifier', 'config': {}}, ...}
actual_module = 'dimos_generated.tf2_msgs.msg.TFMessage'

    def _create_backend(
        self, name: str, payload_type: type[Any] | None = None, **config: Any
    ) -> Backend[Any]:
        validate_identifier(name)
    
        stored = self._registry.get(name)
    
        if stored is not None:
            # Load path: validate type, assemble from stored config
            if payload_type is not None:
                actual_module = f"{payload_type.__module__}.{payload_type.__qualname__}"
                if actual_module != stored["payload_module"]:
>                   raise ValueError(
                        f"Stream {name!r} was created with type {stored['payload_module']}, "
                        f"but opened with {actual_module}"
                    )
E                   ValueError: Stream 'tf' was created with type dimos.msgs.tf2_msgs.TFMessage.TFMessage, but opened with dimos_generated.tf2_msgs.msg.TFMessage

__class__  = <class 'dimos.memory.store.sqlite.SqliteStore'>
actual_module = 'dimos_generated.tf2_msgs.msg.TFMessage'
config     = {'eager_blobs': False, 'must_exist': True, 'page_size': 256, 'path': '.../dimos/data/alfred_stereo_short.db'}
name       = 'tf'
payload_type = <class 'dimos_generated.tf2_msgs.msg.TFMessage'>
self       = <dimos.memory.store.sqlite.SqliteStore object at 0x794df55496d0>
stored     = {'blob_store': {'class': 'dimos.memory.blobstore.sqlite.SqliteBlobStore', 'config': {'path': None}}, 'codec_id': 'lcm', 'eager_blobs': False, 'notifier': {'class': 'dimos.memory.notifier.subject.SubjectNotifier', 'config': {}}, ...}

.../memory/store/sqlite.py:161: ValueError
dimos.core.test_async_module_rpc_sync_to_async::test_async_module_rpc_sync_to_async[zenoh]
Stack Traces | 14.1s run time
start_module = None
in_transport = <dimos.core.transport.pZenohTransport object at 0x7f01c567ac50>
out_transport = <dimos.core.transport.pZenohTransport object at 0x7f01c567a710>

    def test_async_module_rpc_sync_to_async(start_module, in_transport, out_transport):
        """
        Test that you can call a synchronous RPC from an asynchronous RPC and vice versa.
        """
        queue = Queue()
        out_transport.subscribe(queue.put)
        in_transport.publish(4)
>       cubed = queue.get(timeout=0.1)

in_transport = <dimos.core.transport.pZenohTransport object at 0x7f01c567ac50>
out_transport = <dimos.core.transport.pZenohTransport object at 0x7f01c567a710>
queue      = <queue.Queue object at 0x7f01c5678790>
start_module = None

dimos/core/test_async_module_rpc_sync_to_async.py:111: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <queue.Queue object at 0x7f01c5678790>, block = True, timeout = 0.1

    def get(self, block=True, timeout=None):
        '''Remove and return an item from the queue.
    
        If optional args 'block' is true and 'timeout' is None (the default),
        block if necessary until an item is available. If 'timeout' is
        a non-negative number, it blocks at most 'timeout' seconds and raises
        the Empty exception if no item was available within that time.
        Otherwise ('block' is false), return an item if one is immediately
        available, else raise the Empty exception ('timeout' is ignored
        in that case).
        '''
        with self.not_empty:
            if not block:
                if not self._qsize():
                    raise Empty
            elif timeout is None:
                while not self._qsize():
                    self.not_empty.wait()
            elif timeout < 0:
                raise ValueError("'timeout' must be a non-negative number")
            else:
                endtime = time() + timeout
                while not self._qsize():
                    remaining = endtime - time()
                    if remaining <= 0.0:
>                       raise Empty
E                       _queue.Empty

block      = True
endtime    = 746.579999305
remaining  = -0.00011680600005092856
self       = <queue.Queue object at 0x7f01c5678790>
timeout    = 0.1

../...../_temp/uv-python-dir/cpython-3.10-linux-x86_64-gnu/lib/python3.10/queue.py:179: Empty

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

This branch was successfully deployed

1 active deployment
cachix — b40967da Deployed Oct 1, 2026 by TomCC7 via cachix-build #11295
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.

1 participant