Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion dimos/message_codegen/test_builtin_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def test_unlisted_sources_do_not_expand_the_public_catalog(tmp_path):
manifest = json.loads(
(source / "dimos_generated_schemas/package/message-package.json").read_text()
)
assert len(manifest["owned"]) == 83
assert len(manifest["owned"]) == 84
assert "extra_msgs/msg/NotExported" not in manifest["owned"]
assert "builtin_interfaces/msg/Time" in manifest["owned"]

Expand Down
2 changes: 1 addition & 1 deletion dimos/message_codegen/test_builtin_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
assert type(r.header) is Header and r == m
assert type(pickle.loads(pickle.dumps(m)).header) is Header
assert 'MSG: std_msgs/Header' in schema(m.__msgtype__)
assert len(message_types()) == 83
assert len(message_types()) == 84
assert 'visualization_msgs/msg/Marker' not in message_types()
root = Path(package_root())
assert (root / 'schema-sources.json').is_file()
Expand Down
6 changes: 3 additions & 3 deletions dimos/message_codegen/test_builtin_preparation.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from dimos_cdrgen.source_package import configuration, prepare as generate
import pytest

from scripts.prepare_builtin_sources import prepare

Check failure on line 25 in dimos/message_codegen/test_builtin_preparation.py

View workflow job for this annotation

GitHub Actions / lint

DEP003

'scripts' imported but it is a transitive dependency


def test_unprepared_product_fails_and_cached_preparation_is_complete(tmp_path, monkeypatch):
Expand All @@ -48,12 +48,12 @@

monkeypatch.setattr("dimos_cdrgen.source_inputs.requests.get", no_network)
inputs = prepare(cache, offline=True, project=project)
assert len(list(inputs.rglob("*.msg"))) == 83
assert len(list(inputs.rglob("*.msg"))) == 84
output = generate(project)
resources = output / "dimos_generated_schemas/package"
manifest = json.loads((resources / "message-package.json").read_text())
assert len(manifest["owned"]) == 83
assert len(list(project.glob("custom/dimos_msgs/msg/*.msg"))) == 21
assert len(manifest["owned"]) == 84
assert len(list(project.glob("custom/dimos_msgs/msg/*.msg"))) == 22
lock = json.loads((project / "schema-sources.json").read_text())
assert all(len(item["sha256"]) == 64 for item in lock["sources"])
assert (resources / "dimos_msgs-LICENSE").is_file()
Expand Down
44 changes: 44 additions & 0 deletions examples/episode_status/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# EpisodeStatus

Add `packages/dimos-generated/custom/dimos_msgs/msg/EpisodeStatus.msg`:

```text
float64 ts
string state
int64 episodes_saved
int64 episodes_discarded
string last_event "init"
string[] task_label
```

Add the type to `packages/dimos-generated/catalog.json`, then prepare and generate locally. Commit the definition and catalog; generated output stays ignored:

```sh
uv run python -m scripts.prepare_builtin_sources
uv run python -m scripts.generate_builtin_messages
uv run python -m scripts.generate_builtin_messages --check
```

Use the generated Python value from the editable checkout or matching message wheel:

```python
from dimos_generated.dimos_msgs.msg import EpisodeStatus
from dimos_cdrgen.registry import encode, decode

status = EpisodeStatus(ts=17.25, state="recording", episodes_saved=2,
episodes_discarded=1, last_event="start", task_label=["inspection"])
assert len(status.task_label) <= 1
assert decode(encode(status), EpisodeStatus) == status
```

`ts` is seconds. `task_label=[]` represents None; `[""]` preserves an empty label.
The published Python backend cannot encode bounded sequences. The unbounded sequence
has the same CDR bytes; applications enforce zero or one label before encoding or after decoding.
Counters use signed 64-bit storage. Applications enforce finite timestamps and
`state` (`idle/recording`) and `last_event` (`start/save/discard/init`) values.
Generated values require explicit fields and retain their wire values without Pydantic coercion. The source model's JSON `schema_version` envelope is not a
message field; adding this type does not migrate the robot-learning collector.

The fields follow the [EpisodeStatus model](https://github.com/dimensionalOS/dimos/blob/ef5e5f2710c482fb1d43d43ec50fd73b0fbe1dde/dimos/imitation/collection/episode.py).
See the [built-in message guide](/docs/development/messages-in-repository.md) for
checkout prerequisites and the existing package/consumer workflow.
60 changes: 60 additions & 0 deletions examples/episode_status/test_episode_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# 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.

"""The supported sequence schema retains the nullable label wire contract."""

import sys

from dimos_cdrgen.registry import decode, encode, schema
from dimos_generated.dimos_msgs.msg import EpisodeStatus
import pytest
from rosbags.typesys import Stores, get_types_from_msg, get_typestore


@pytest.fixture(autouse=True)
def restore_reference_types_module():
previous = sys.modules.get("rosbags.usertypes")
try:
yield
finally:
if previous is None:
sys.modules.pop("rosbags.usertypes", None)
else:
sys.modules["rosbags.usertypes"] = previous


@pytest.mark.parametrize("labels", [[], [""], ["inspection"]])
def test_nullable_labels_and_signed_counters_preserve_bounded_wire_layout(labels):
message = EpisodeStatus(
ts=17.25,
state="recording",
episodes_saved=-2,
episodes_discarded=-1,
last_event="start",
task_label=labels,
)
reference = get_typestore(Stores.EMPTY)
bounded = schema(message.__msgtype__).replace("string[] task_label", "string[<=1] task_label")
reference.register(get_types_from_msg(bounded, message.__msgtype__))
wire = encode(message)
assert wire == bytes(reference.serialize_cdr(message, message.__msgtype__))
restored = decode(wire, EpisodeStatus)
assert restored.task_label == labels
assert (restored.ts, restored.state, restored.episodes_saved, restored.episodes_discarded) == (
17.25,
"recording",
-2,
-1,
)
assert restored.last_event == "start"
3 changes: 2 additions & 1 deletion packages/dimos-generated/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"dimos_msgs/msg/ControlValues": "Published runtime consumer: dimos/control/contract/validate.py",
"geometry_msgs/msg/PoseWithCovarianceStamped": "Published native runtime consumer: dimos/experimental/memory/rust/src/decoding.rs",
"geometry_msgs/msg/TwistWithCovarianceStamped": "Published native runtime consumer: dimos/experimental/memory/rust/src/decoding.rs",
"geometry_msgs/msg/WrenchStamped": "Published native runtime consumer: dimos/experimental/memory/rust/src/decoding.rs"
"geometry_msgs/msg/WrenchStamped": "Published native runtime consumer: dimos/experimental/memory/rust/src/decoding.rs",
"dimos_msgs/msg/EpisodeStatus": "Built-in authoring example: examples/episode_status/README.md"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# EpisodeStatus internal model from robot-learning PR4343, ef5e5f2710c482fb1d43d43ec50fd73b0fbe1dde.
# Seconds; application validation requires a finite value.
float64 ts
# Application values: idle, recording. Required by the source model.
string state
# Source Python ints use signed 64-bit wire storage; negative values are retained.
int64 episodes_saved
int64 episodes_discarded
# Application values: start, save, discard, init.
string last_event "init"
# Nullable string: [] means None; [""] preserves an explicitly empty label.
# Applications validate at most one element; the published Python backend cannot encode bounded sequences.
string[] task_label
Loading