Skip to content

The ROS distribution is one switch: jazzy/lyrical features, distro-free packages, runtime in every bag, Lyrical CI rehearsal - #8

Merged
mbreiser merged 2 commits into
mainfrom
feat/ros-distro-switch
Nov 5, 2026
Merged

mbreiser merged 2 commits into
mainfrom
feat/ros-distro-switch

Conversation

@mbreiser

@mbreiser mbreiser commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to the rig-nodes extraction (LAB-338): the ROS 2 distribution becomes one switch, and CI rehearses the next one. Nothing a rig runs changes: every workspace environment the rigs install stays Jazzy (RoboStack mutex 0.19, Python 3.12) and resolves to the same packages; the channel keeps publishing Jazzy only. Record: docs/ros-distro-switch.md.

What changes

  • Workspace pixi.toml: [feature.jazzy] (default) and [feature.lyrical] (opt-in, pixi install -e lyrical) each carry the RoboStack channel (appended behind conda-forge), the ros2-distro-mutex pin, the Python pin and that distro's spelling of the ROS packages the workspace installs. [workspace.build-variants] ros2-distro-mutex = ["0.19.*|0.22.*"] pins the source packages' host solve with ONE disjunctive value that each channel narrows to its set (a two-element list fails: pixi solves every variant value in every environment, and 0.22.* has no Jazzy candidate).
  • The package manifests name no distro. src/rig_interfaces/pixi.toml drops distro = "jazzy" and the ros-jazzy-* host/run lines: pixi-build-ros reads the robostack-<distro> channel and maps package.xml itself; the ROS packages' run_exports pin the result (ros2-distro-mutex >=0.19.0,<0.20.0a0 + python_abi 3.12.* *_cp312 on Jazzy, >=0.22.0,<0.23.0a0 + cp314 on Lyrical — both in the lock, from one manifest). src/rig_nodes/pixi.toml depends on RoboStack's distro-neutral ros2-rclpy / ros2-std-msgs / ros2-rosbag2-py / ros2-rosbag2-storage-mcap / ros2-rosidl-runtime-py (the ros-<distro>-* names are 2 KiB shims onto them in both channels); pixi-build-python carries no run_exports, so the noarch package names the mutex bare and follows the consumer's set. CI's inspect step checks the ros2-* dependencies for rig_nodes and the pin for rig_interfaces.
  • Every bag says what it ran on: new rig_nodes/runtime.py (runtime_info(): ROS distro, rmw + version, rclpy/rosbag2 versions, the RoboStack set as <version> <build> from conda-meta, Python, platform, package-set versions; pixi run runtime prints it). Recorder.open() hands it to rosbag2 as custom_data; rosbag2 writes it with its own ros_distro into the MCAP metadata record named rosbag2 at close, mcap_index.reindex keeps it, close() returns it as runtime. Checked by the device-node test on both distros.
  • Recorder portability fix found by the rehearsal: rosbag2_py 0.33 (Lyrical) gave the 3-argument write overload an optional sequence_number, so our positional 4-argument write bound the producer stamp to it and publish_time silently equaled log_time. The recorder now passes the stamps by keyword where the binding names them (Jazzy's binding does not).
  • Zenoh configs: zenoh.config_dir() selects config/<ROS_DISTRO>/ when a complete pair is checked in there, else the shared pair, and apply_env_defaults uses it. Deriving a Lyrical pair found the stock rmw_zenoh files at 0.2.11 (Jazzy) and 0.10.6 (the version RoboStack packages for Lyrical) byte-identical, so one pair ships; upstream's lyrical branch (0.10.8) does change the schema, and the doc says how to add the pair that day.
  • _common.py / bias_cmd.py: the two "Jazzy" mentions are comments (rclpy shutdown races, the Windows clock), left as they are.
  • CI (package-set.yml): the release path (build → channel → clean-directory accept) stays Jazzy, with distro / mutex / python spelled once per job matrix. New allowed-to-fail job lyrical rehearsal: pixi install -e lyrical on win-64 / osx-arm64 / linux-64 (builds both packages for that set in place), then build_interfaces --check, test-pure, test-host, test-sim, test (device-node conformance, simulators only). No channel payload. PIXI_VERSION 0.80.0 → 0.83.0 (the rigs' floor since 2026-10-28; the pixi that writes pixi.lock, which the new job installs).
  • Docs: docs/ros-distro-switch.md (what differs between sets, where the switch lives, Lyrical availability per platform, what blocks a migration, rehearsal results), README, SETUP (recipe now declares ros-base + the rmw explicitly), CONTRACT rule 5 (the distro switch; which package carries the pin), channel/README.md.

RoboStack Lyrical availability (2026-11-05)

ros-lyrical-rosbag2-storage-mcap 0.33.4, ros-lyrical-rmw-zenoh-cpp 0.10.6 and ros-lyrical-foxglove-bridge 3.6.0 exist for win-64, osx-arm64 and linux-64, as does everything else the two workspaces need (ros-base, rosbag2-py, rclpy, rosidl toolchain, ros-environment, rqt, demo-nodes-py). Mutex is 0.22 (lyrical_30; 0.21 lyrical_28 exists too), Python 3.14 only. Nothing missing on any platform.

Results on this Mac (osx-arm64, pixi 0.83.0)

Jazzy Lyrical
test-pure / test-host / test-sim 50/50 · 52/52 · 39/39 50/50 · 52/52 · 39/39
test (device-node conformance) 42/42 42/42 (41/42 before the rosbag2_py fix)
build_interfaces --check ✓ ✓
pixi install (warm) 28 s 32 s

CI results on the three platforms: see the checks on this PR (summary to follow in a comment).

What blocks a real migration (detail in the doc, §7)

Python 3.14 for the lab's own scripts (nothing of ours has run on it besides this package set); RoboStack Lyrical's channel is weeks old and still being rebuilt; rmw_zenoh 0.10.6 is behind upstream's Lyrical branch (0.10.8, new config schema); one distro per host (a Jazzy node and a Lyrical node do not talk); nothing has run on hardware under Lyrical; the channel is Jazzy-only; binding drift like the rosbag2_py overload above. And staying on Jazzy is not standing still: RoboStack's current Jazzy rebuild is mutex 0.20 on Python 3.14, so the next re-pin brings 3.14 either way.

Companion: reiserlab/webDisplayTools#294 mirrors the features in rig/pixi.toml (rigs' environments byte-identical in the lock; its lyrical environment builds this branch from git, and the arena harness passed 63/63 on it — Python 3.14, rosbag2 0.33). Not a package-set release; main stays 0.4.0.

🤖 Generated with Claude Code

mbreiser and others added 2 commits November 5, 2026 10:45
…ee packages, runtime in every bag, Lyrical CI rehearsal

Workspace: `[feature.jazzy]` (default, what the rigs run) and `[feature.lyrical]` (opt-in) each
carry the RoboStack channel (appended behind conda-forge), the ros2-distro-mutex pin, the Python
pin and that distro's ROS package names; `[workspace.build-variants]` pins the source packages'
host solve with ONE disjunctive value (`0.19.*|0.22.*`) that each channel narrows to its set.

Packages name no distro: pixi-build-ros reads the `robostack-<distro>` channel and maps
package.xml itself (rig_interfaces comes out cp312 + mutex >=0.19,<0.20 on Jazzy, cp314 +
>=0.22,<0.23 on Lyrical from one manifest); rig_nodes depends on RoboStack's distro-neutral
`ros2-*` names and, being noarch, names the mutex bare and follows the consumer's set.

Code: `rig_nodes.runtime.runtime_info()` (ROS distro, rmw + version, RoboStack set, Python,
package-set versions) goes into every bag as rosbag2 custom_data via Recorder.open(); the
recorder passes the two stamps by keyword where rosbag2_py names them — on Lyrical the positional
4-argument write bound the producer stamp to the new `sequence_number` and publish_time silently
equaled log_time. `zenoh.config_dir()` selects `config/<ROS_DISTRO>/` when present; the stock
rmw_zenoh files at 0.2.11 and 0.10.6 are byte-identical, so one pair ships.

CI: the release path stays Jazzy (distro/mutex/python spelled once per matrix); a new
allowed-to-fail `lyrical` job installs the lyrical environment on the three platforms and runs the
four simulator-only suites. PIXI_VERSION → 0.83.0 (the rigs' floor; writes pixi.lock).

Docs: docs/ros-distro-switch.md (the switch, Lyrical availability per platform, what blocks a
migration, rehearsal results), README, SETUP, CONTRACT rule 5, channel/README.

Mac results: Jazzy pure 50/50, host 52/52, sim 39/39, device-node 42/42; Lyrical the same four
(device-node 41/42 before the rosbag2_py fix). LAB-338 follow-up.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ot the hash build string

Run 42058302847 built both packages with the right pins on osx-arm64 and linux-64
(`ros2-distro-mutex >=0.19.0,<0.20.0a0`) and failed only on the new inspect check, which looked for
`py312` in a build string that is a bare hash. The compiled-against Python is the package's
`python_abi 3.12.* *_cp312` dependency. Also: a consumer workspace that builds the source packages
must carry the mutex build-variant (doc §2).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@mbreiser

mbreiser commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

CI so far (2026-11-05)

Run 42058302847 on b5283e2: the three Jazzy builds produced packages with the right pins (ros2-distro-mutex >=0.19.0,<0.20.0a0 on rig_interfaces, so pixi-build-ros's distro detection works on the pixi build --path path too) and failed only on my new inspect check, which looked for py312 in a build string that is a bare hash — fixed in 60b9ee1 (the check reads python_abi 3.12.* *_cp312 instead). Lyrical rehearsal in that run: osx-arm64 ✓, linux-64 ✓ — build_interfaces --check serializes, test-pure 50/50, test-host 52/52, test-sim 39/39, device-node test 42/42 on mutex 0.22.0 lyrical_30 / Python 3.14.10.

Run 42058992937 on 60b9ee1: build osx-arm64 ✓, linux-64 ✓, win-64 running; channel + accept follow. Lyrical rehearsal linux-64 ✓ again; osx-arm64 ✗ on one host-sampler check — disk busy = (Δread_time + Δwrite_time) / window, clamped to 1: got 0.967, expected 1.0 (51/52) — the same test passed on osx-arm64 in the first run and on linux-64 in both, so a timing flake of test_host_sampler.py's busy-window arithmetic on a loaded runner, not a Lyrical effect; worth a look in its own change. win-64 rehearsal running.

Local (Mac, osx-arm64): Jazzy 50/52/39/42, Lyrical 50/52/39/42 (device-node 41/42 before the rosbag2_py keyword fix). webDisplayTools#294's lyrical environment built this branch from git and ran the arena harness 63/63 on Python 3.14.

@mbreiser
mbreiser merged commit f7bb3fd into main Nov 5, 2026
9 of 10 checks passed
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