Skip to content

refactor: retire unused legacy message code with M20 gate pending (7/7) - #4365

Closed
TomCC7 wants to merge 11 commits into
feat/cdr-candidate-record-replayfrom
feat/cdr-candidate-retirement
Closed

TomCC7 wants to merge 11 commits into
feat/cdr-candidate-record-replayfrom
feat/cdr-candidate-retirement

Conversation

@TomCC7

@TomCC7 TomCC7 commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Unused old rich message classes, obsolete interoperability implementations and the root Python dimos-lcm dependency are removed after consumers migrate. Raw LCM transport remains intentionally. This is partial retirement: the proprietary M20 onboard DrDDS bridge still uses the old generated message repository and cannot be declared migrated without its ABI-compatible AArch64 SDK/toolchain.

Base: #4364. This is layer 7/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: 060d4e13f32b76360c0905981967fe22f40a6539.

Validation: 17 authoring and 97 offline runtime tests passed (4 LCM-named cases deselected); native CMake archive/Cargo crate built offline and relocated package acceptance passed; lock and hooks passed. The original publication checkpoint matched preserved complete reference cb98281 (tree 28ff1e5e6fbe0e95400e845770dce3936f553ac0). Subsequent differences are explicitly limited to the approved two-tutorial documentation, its validation/workflow changes, the shared CDR reference test dependency/lock, the shared CDR reference test dependency/lock, the shared CDR reference test dependency/lock, and four import-formatting fixes from autofix; no production API changes were introduced. 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 #4371. Exact commit 060d4e13f32b76360c0905981967fe22f40a6539, 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

❌ 5 Tests Failed:

Tests completed Failed Passed Skipped
6367 5 6362 94
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 0x75eff6b6b680>
.../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 0x75eff6b6b680>
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 0x75eff6b6b680>
        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 0x75eff6b6b680>
        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 0x75eff6b6b680>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <dimos.memory.store.sqlite.SqliteStore object at 0x75eff6b6b680>
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 0x75eff6b6b680>
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.teleop.hosted.test_map_compress::test_costmap_rate_gated
Stack Traces | 0.746s run time
module = <dimos.teleop.hosted.map_compress.MapCompressModule object at 0x7fbb4f3d4bf0>

    def test_costmap_rate_gated(module: MapCompressModule) -> None:
        module._on_costmap(_occupancy([[0, 0]]))
        first = len(module.map_out.publish.call_args_list)
        module._on_costmap(_occupancy([[0, 0]]))  # immediately again → gated out
>       assert len(module.map_out.publish.call_args_list) == first
E       assert 2 == 1
E        +  where 2 = len([call(b'{"type":"map","fmt":"png","w":2,"h":1,"res":0.10000000149011612,"origin":[0.0,0.0],"stamp":0.0,"png_b64":"iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAYAAAD0In+KAAAAD0lEQVQIHWOUs3L5zwAEAAkrAZ0U7a2zAAAAAElFTkSuQmCC"}'),\n call(b'{"type":"map","fmt":"png","w":2,"h":1,"res":0.10000000149011612,"origin":[0.0,0.0],"stamp":0.0,"png_b64":"iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAYAAAD0In+KAAAAD0lEQVQIHWOUs3L5zwAEAAkrAZ0U7a2zAAAAAElFTkSuQmCC"}')])
E        +    where [call(b'{"type":"map","fmt":"png","w":2,"h":1,"res":0.10000000149011612,"origin":[0.0,0.0],"stamp":0.0,"png_b64":"iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAYAAAD0In+KAAAAD0lEQVQIHWOUs3L5zwAEAAkrAZ0U7a2zAAAAAElFTkSuQmCC"}'),\n call(b'{"type":"map","fmt":"png","w":2,"h":1,"res":0.10000000149011612,"origin":[0.0,0.0],"stamp":0.0,"png_b64":"iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAYAAAD0In+KAAAAD0lEQVQIHWOUs3L5zwAEAAkrAZ0U7a2zAAAAAElFTkSuQmCC"}')] = <MagicMock name='publish' id='140442457596192'>.call_args_list
E        +      where <MagicMock name='publish' id='140442457596192'> = <dimos.core.stream.Out object at 0x7fbb4f3d5610>.publish
E        +        where <dimos.core.stream.Out object at 0x7fbb4f3d5610> = <dimos.teleop.hosted.map_compress.MapCompressModule object at 0x7fbb4f3d4bf0>.map_out

first      = 1
module     = <dimos.teleop.hosted.map_compress.MapCompressModule object at 0x7fbb4f3d4bf0>

.../teleop/hosted/test_map_compress.py:101: AssertionError
dimos.mapping.dim_slam.test_dim_odom_replay::test_dim_odom_stereo_replay
Stack Traces | 1.37s 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 0x75eea36fbdd0>
.../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 0x75eea36fbdd0>
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 0x75eea36fbdd0>
        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 0x75eea36fbdd0>
        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 0x75eea36fbdd0>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <dimos.memory.store.sqlite.SqliteStore object at 0x75eea36fbdd0>
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 0x75eea36fbdd0>
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.e2e_tests.test_dimsim_spatial_memory::test_go_to_the_bed
Stack Traces | 331s run time
lcm_spy = <dimos.e2e_tests.lcm_spy.LcmSpy object at 0x75eea1fc5430>
start_blueprint = <function start_blueprint.<locals>.set_name_and_start at 0x75eea19eede0>
wait_for_system_ready = <function wait_for_system_ready.<locals>.wait at 0x75eea19ed6c0>
human_input = <function human_input.<locals>.send_human_input at 0x75eea19ef7e0>
dim_sim = <dimos.e2e_tests.dim_sim_client.DimSimClient object at 0x75efd85cbbf0>
explore_house = <function explore_house.<locals>.explore at 0x75eea19ee700>

    @pytest.mark.self_hosted_large
    def test_go_to_the_bed(
        lcm_spy, start_blueprint, wait_for_system_ready, human_input, dim_sim, explore_house
    ) -> None:
        start_blueprint(
            "run",
            "unitree-go2-agentic",
            simulator="dimsim",
        )
        wait_for_system_ready(timeout=1200.0)
    
        explore_house()
    
        human_input("go to the bed")
    
>       lcm_spy.wait_until_odom_position(-3.567, -1.332, threshold=2, timeout=180)

dim_sim    = <dimos.e2e_tests.dim_sim_client.DimSimClient object at 0x75efd85cbbf0>
explore_house = <function explore_house.<locals>.explore at 0x75eea19ee700>
human_input = <function human_input.<locals>.send_human_input at 0x75eea19ef7e0>
lcm_spy    = <dimos.e2e_tests.lcm_spy.LcmSpy object at 0x75eea1fc5430>
start_blueprint = <function start_blueprint.<locals>.set_name_and_start at 0x75eea19eede0>
wait_for_system_ready = <function wait_for_system_ready.<locals>.wait at 0x75eea19ed6c0>

dimos/e2e_tests/test_dimsim_spatial_memory.py:33: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/e2e_tests/lcm_spy.py:191: in wait_until_odom_position
    self.wait_for_message_result(
        predicate  = <function LcmSpy.wait_until_odom_position.<locals>.predicate at 0x75eea1963420>
        self       = <dimos.e2e_tests.lcm_spy.LcmSpy object at 0x75eea1fc5430>
        threshold  = 2
        timeout    = 180
        x          = -3.567
        y          = -1.332
dimos/e2e_tests/lcm_spy.py:177: in wait_for_message_result
    wait_until(
        event      = <threading.Event at 0x75eea0902750: unset>
        fail_message = 'Failed to get to position x=-3.567, y=-1.332'
        listener   = <function LcmSpy.wait_for_message_result.<locals>.listener at 0x75eea19632e0>
        predicate  = <function LcmSpy.wait_until_odom_position.<locals>.predicate at 0x75eea1963420>
        self       = <dimos.e2e_tests.lcm_spy.LcmSpy object at 0x75eea1fc5430>
        timeout    = 180
        topic      = '/odom#geometry_msgs/msg/PoseStamped'
        type       = <class 'dimos_generated.geometry_msgs.msg.PoseStamped'>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

predicate = <bound method Event.is_set of <threading.Event at 0x75eea0902750: unset>>

    def wait_until(
        predicate: Callable[[], bool],
        *,
        timeout: float,
        interval: float = 0.1,
        message: str | None = None,
    ) -> None:
        """Poll ``predicate`` until it returns truthy or ``timeout`` elapses."""
        deadline = time.monotonic() + timeout
        while time.monotonic() < deadline:
            if predicate():
                return
            time.sleep(interval)
>       raise TimeoutError(message or f"Timed out after {timeout}s waiting for condition")
E       TimeoutError: Failed to get to position x=-3.567, y=-1.332

deadline   = 7314464.166161882
interval   = 0.1
message    = 'Failed to get to position x=-3.567, y=-1.332'
predicate  = <bound method Event.is_set of <threading.Event at 0x75eea0902750: unset>>
timeout    = 180

.../utils/testing/waiting.py:35: TimeoutError

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 — 060d4e13 Deployed Oct 1, 2026 by TomCC7 via cachix-build-macos #11297
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