Skip to content

Zenoh support - #73

Open
victorpaleologue wants to merge 33 commits into
Atostek:masterfrom
semio-ai:zenoh_support
Open

Zenoh support#73
victorpaleologue wants to merge 33 commits into
Atostek:masterfrom
semio-ai:zenoh_support

Conversation

@victorpaleologue

@victorpaleologue victorpaleologue commented Jul 22, 2026

Copy link
Copy Markdown

Introduced flags to select zenoh or dds as the RMW.
DDS route mainly unchanged.
Zenoh route tested with official ROS 2 tools.
Closes #71
Supersedes #72 (was coming from semio-ai:master, but it was a moving branch)

claude and others added 30 commits July 7, 2026 13:02
…hop use cases)

Background research collected as ground-truth references for the Zenoh
backend study: rmw_zenoh key-expression/liveliness/attachment/service model,
the Zenoh 1.x Rust API surface, and the RosCon 2025 workshop use cases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
- docs/zenoh_study/{README,feature_map,refactoring_plan,test_plan}.md
- docs/decisions/0001..0009 ADRs (feature architecture, CDR reuse,
  owned public types, liveliness discovery, queryable services,
  type-hash interop, GID/attachment parity, router/config)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
Captured because the session has read-only GitHub access (issue/PR/push
writes are policy-denied); create from this file once access is granted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
Introduce the mutually-exclusive middleware backend features per ADR-0002:
- Cargo: add `dds` (default) and `zenoh` features; make `rustdds` optional
  and gated behind `dds`; add optional `zenoh`/`zenoh-ext` deps (resolve to
  zenoh 1.9); `security` now implies `dds`; default = ["dds", "jazzy"].
- lib.rs: compile_error! guard requiring exactly one backend; gate the crate
  root `rustdds` re-exports and `ros2::` DDS types behind `dds`.
- Add `src/zenoh_backend/` module home (cfg `zenoh`) for E2-E6.

The default DDS build is unchanged and green (9 lib tests pass, fmt clean);
the Zenoh backend compile lands with E1-E3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
Backend-neutral 'spec' modules under src/zenoh_backend/, compiled on every
build so they are unit-testable without the zenoh crate or a network:

- keyexpr: data-plane topic/service keys <domain>/<name>/<type>/<hash> and
  @ros2_lv liveliness keys (NN/MP/MS/SS/SC) with %-mangling; tests reproduce
  the exact key strings from the rmw_zenoh design examples.
- type_hash: REP-2016 known-types table + wildcard-receive strategy (ADR-0007).
- gid: 16-byte entity GID = XXH3-128(liveliness key) as low64||high64 LE
  (ADR-0008); byte-layout and determinism tests.

Adds xxhash-rust dep. 22 lib tests pass (11 new); fmt + clippy clean; default
DDS build unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
CI formats with nightly rustfmt honoring wrap_comments/imports_granularity;
reflow comments to match so the format check passes.
…tvgj

Zenoh backend: study, plan, decisions + E0/E2 foundations
Introduce ros2_client::qos::QosProfile — a backend-neutral ROS 2 QoS profile
(reliability/durability/history/deadline/lifespan/liveliness/lease) that does
not name any rustdds type, per ADR-0004. On the dds backend it converts to/from
rustdds::QosPolicies (round-trip tested); on zenoh it will drive pub/sub options
and the liveliness QoS encoding.

Additive: existing APIs still take rustdds QoS; migration is incremental.
Re-exported as ros2_client::QosProfile and ros2::QosProfile. 3 unit tests
(incl. rustdds round-trip); fmt/clippy/doc clean.

Refs #4 (E1), #2.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
The publisher unwrapped its first async_publish, which a Reliable writer can
legitimately fail with WouldBlock before a matching subscription is discovered
(a race exposed on fast loopback CI runners; surfaced now that this fork runs CI
for the first time). Tolerate the transient error and keep publishing; the
subscriber only needs one message within its timeout.

Refs #2.
…4tvgj

E1: owned QosProfile type (decouple public API from RustDDS)
Make the crate compile under `--no-default-features --features zenoh` and add
the first real Zenoh backend module:

- lib.rs: gate all RustDDS-coupled modules/re-exports (context, node, pubsub,
  service, action, message_info, parameters, rcl_interfaces, rosout,
  builtin_topics, entities_info, gid, interfaces, lazy_static) behind `dds`;
  gate the RustDDS Timestamp conversions in ros_time and the `Log` struct in
  log (keeping `LogLevel`); allow dead_code on the zenoh build for neutral
  helpers not yet wired to the zenoh side.
- zenoh_backend::context: `Context`/`ContextOptions` opening one
  `zenoh::Session` per context (E3). Default peer config listens on IPv4
  loopback with multicast off (rmw_zenoh shape; also works where IPv6 is
  unavailable, e.g. CI). Full JSON5/env config is a follow-up TODO.
- CI: new tests-zenoh.yml (check + clippy -D warnings + lib tests for the
  zenoh feature; guards dds/zenoh exclusivity).

DDS build unchanged (23 lib tests). Zenoh: 18 lib tests pass incl. opening a
real session. Refs #6 (E3), #5 (E2), #2.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
…4tvgj

E3: Zenoh Context/session + compile the `zenoh` feature + CI
zenoh_backend::attachment::AttachmentData carries (i64 sequence, i64
source_timestamp_ns, [u8;16] source_gid) via the zenoh-ext serializer, matching
rmw_zenoh's exact wire layout: 8B LE seq | 8B LE ts | 0x10 | 16B gid (33 bytes).
Unit tests pin the byte layout, round-trip, and truncation rejection (ADR-0008).

Refs #5 (E2), #2.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
…4tvgj

E2: message attachment (rmw_zenoh byte-compatible)
zenoh_backend::cdr provides to_cdr/from_cdr: message body via cdr-encoding
(same author as RustDDS) plus the 4-byte encapsulation header prepended
ourselves (cdr-encoding emits the body only). to_cdr writes CDR_LE; from_cdr
honours the representation identifier's endianness (LE/BE incl. PL variants).
Tests cover round-trip (String + struct), header-present-once, big-endian
decode, and header validation (ADR-0003).

Adds cdr-encoding + byteorder deps under the zenoh feature. Unblocks E4 pub/sub.

Refs #5 (E2), #7 (E4), #2.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
cargo-audit flagged RUSTSEC-2026-0041 (lz4_flex <0.11.6, pulled by zenoh's
compression; zenoh pins 0.10.x) and RUSTSEC-2023-0071 (rsa 0.9 Marvin timing
side-channel, via zenoh's TLS stack; no upstream patch exists). Both are
transitive, only affect the optional zenoh feature, and are unresolvable
without an upstream zenoh bump. Ignore them with justification in
.cargo/audit.toml and re-trigger the audit workflow on changes to it.

Refs #2.
Minimal Zenoh node surface + data plane:
- Context::new_node -> Node; Node::create_topic/create_publisher/
  create_subscription. Topic resolves the fully-qualified ROS name.
- Publisher<M>: put(CDR payload + attachment(seq,ts,gid)); concrete key with
  real-or-placeholder type hash. Subscription<M>: declare_subscriber with a
  wildcard type hash (liberal receive), decodes CDR + attachment into
  MessageInfo. Per-publisher sequence numbers; entity GID from liveliness key.
- type_hash::sender_hash now returns a concrete PLACEHOLDER_HASH (a put can't
  target a wildcard key); receivers keep using WILDCARD.
- Re-export Node/NodeOptions/Topic/Publisher/Subscription/MessageInfo under zenoh.

Tier B in-process test: two contexts connected over loopback exchange a
std_msgs/String round-trip. 27 zenoh lib tests pass; dds unchanged (23);
fmt + clippy (both backends) clean.

Refs #7 (E4), #2.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
zenoh_backend::qos_encoding encodes/decodes the <qos> component of liveliness
tokens (rmw_zenoh format, 6 colon-groups). Every field except depth is
delta-encoded vs the RMW default profile; depth is always emitted. Reproduces
the documented examples exactly (reliable/volatile/keep-last depth 7 ->
'::,7:,:,:,,') and round-trips. Pure module, tested on both backends.

Refs #8 (E5), #2.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
- keyexpr::parse_liveliness_key: inverse of the token builders (ParsedEntity;
  names demangled), with round-trip + rejection tests.
- Node declares a node (NN) liveliness token on creation; create_publisher/
  create_subscription declare MP/MS tokens carrying the compact QoS encoding
  (E5a); the entity GID is derived from that liveliness key. Tokens are held by
  the Node/Publisher/Subscription (dropped => undeclared). Declaration is
  best-effort (warns, still functional).

Tier-B test: a publisher created on one context is discovered by another via a
liveliness get, its NN + MP tokens parsed back correctly. 35 zenoh lib tests
pass; dds unchanged (30); fmt + clippy (both) clean.

Refs #8 (E5), #2.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
zenoh_backend::graph_cache::GraphCache maintains the live entity set parsed from
liveliness tokens. Context declares a liveliness subscriber over
@ros2_lv/<domain>/** with history(true) and a callback that applies PUT/DELETE
to the cache. New Context queries: publisher_count/subscription_count/node_names.

Tier-B test: a second context discovers a remote publisher + node via the cache
(publisher_count == 1, node_names contains /talker). Pure cache logic also unit
tested. 37 zenoh lib tests pass; dds unchanged (31); fmt + clippy (both) clean.

Refs #8 (E5), #2.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
zenoh_backend::service: Client<Req,Resp> and Server<Req,Resp> implementing ROS 2
services over Zenoh (ADR-0006).
- Server: complete queryable; per-query reads CDR request + attachment
  (seq,ts,client_gid), stores the Query keyed by (client_gid,seq), and replies
  with the CDR response echoing seq+gid. try_receive_request/async_receive_request
  + send_response; RmwRequestId {writer_gid,sequence_number}.
- Client: get on the concrete service key (real hash if known, else placeholder)
  carrying request payload + attachment; decodes the first OK reply. call/async_call.
- Node::create_client/create_server; SC/SS liveliness tokens; names::ServiceTypeName
  gains dds_service_type() (zenoh-only) for the service key type component.

Tier-B test: example_interfaces/AddTwoInts round-trip across two contexts returns
42. 38 zenoh lib tests pass; dds unchanged (31); clippy -D warnings (both) + fmt clean.

Refs #9 (E6), #2.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
zenoh_backend::action: ActionClient/ActionServer composing the ROS 2 action
sub-entities on the Zenoh service + pub/sub layers (ADR / rmw_zenoh §8):
- send_goal + get_result services, feedback topic; goals correlated by an
  application-level GoalId (random UUID).
- Client: send_goal (returns id+accepted), get_result (long timeout, mirroring
  rmw's _action/get_result heuristic; bounded to 60s), take_feedback.
- Server: try_receive_goal/respond_goal, publish_feedback,
  try_receive_result_request/respond_result.
- Node::create_action_client/create_action_server build the sub-entities;
  service.Client gains an optional get timeout.
cancel_goal + status topic are documented follow-ups.

Tier-B test: action_tutorials_interfaces/Fibonacci order 5 -> [0,1,1,2,3] via
send_goal + feedback + get_result across two contexts. 39 zenoh lib tests pass;
dds unchanged (31); clippy -D warnings (both) + fmt clean.

Refs #11 (E7), #2.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
Implement ROS 2 parameters on the Zenoh backend as a composition of six
`rcl_interfaces` services and the `parameter_events` topic, mirroring how
rmw_zenoh / rclcpp expose parameters (there is no middleware-level
parameter concept).

- `ParameterServer`: parameter store + get_parameters, get_parameter_types,
  set_parameters, set_parameters_atomically, list_parameters,
  describe_parameters queryables, plus a `/parameter_events` publisher.
  Application-driven via `spin_once()`/`spin()`, matching the primitives
  idiom of the Zenoh services/actions layers. `set_parameters_atomically`
  is not implemented (fails per entry, as in the DDS backend).
- `ParameterClient`: get/set/list/types/describe against a remote node's
  parameter services.
- `Node::create_parameter_server` / `create_parameter_client`.
- `ParameterEvent` carries an owned `builtin_interfaces::Time` stamp,
  keeping the Zenoh backend independent of RustDDS (ADR-0004); its CDR
  field layout matches `rcl_interfaces/msg/ParameterEvent`.

To reuse the ROS 2 parameter message types across both backends, make the
`parameters` and `rcl_interfaces` modules backend-neutral: only the
DDS-specific pieces (`raw::ParameterEvent`'s `rustdds::Timestamp` and the
`AService` type aliases) stay gated behind the `dds` feature; the value
enums, conversions, and request/response structs now compile on both.

Verified in-process (Tier B) over IPv4 loopback: a client gets the initial
value, sets a changed value and a new parameter, reads them back, queries
types, and lists names (including the built-in `use_sim_time`) with prefix
filtering. 40 zenoh lib tests pass; DDS tests, clippy (both backends), and
nightly rustfmt are clean.

Closes #12

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
Add rosout logging on the Zenoh backend: a `Logger` that publishes
`rcl_interfaces/msg/Log` records to the global `/rosout` topic, plus an
inbound subscription for reading `/rosout` (the `read_rosout` capability).

- `zenoh_backend::rosout::Log`: an owned-timestamp
  (`builtin_interfaces::Time`) log record whose CDR field layout matches
  `rcl_interfaces/msg/Log`, keeping the Zenoh backend independent of
  RustDDS (ADR-0004; the DDS backend's `log::Log` uses `rustdds::Timestamp`).
- `Logger` with `log_at` + level helpers (debug/info/warn/error/fatal),
  stamping the node's base name into each record.
- A `zenoh`-gated `rosout!` macro mirroring the DDS one, capturing the call
  site's file/line (the DDS macro lives in the dds-gated `node` module, so
  the two never collide).
- `Node::create_logger` / `Node::read_rosout`.

Verified in-process (Tier B) over IPv4 loopback: a node logs via `rosout!`
and a second node reads the record back, checking level, name, message, and
source location. 41 zenoh lib tests pass; DDS tests, clippy (both
backends), and nightly rustfmt are clean.

Closes #10

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
Wrap up the Zenoh backend MVP with docs and an example.

- Add a self-contained `zenoh_demo` example: an in-process talker + listener
  over IPv4 loopback (peer mode, no router) that exercises topic pub/sub and
  `/rosout` logging, runnable with
  `cargo run --no-default-features --features zenoh --example zenoh_demo`.
- Declare all examples explicitly in Cargo.toml with `autoexamples = false`.
  The DDS examples are gated `required-features = ["dds"]`, so a
  `--no-default-features --features zenoh --examples` build skips them instead
  of failing; `zenoh_demo` is gated on `zenoh`.
- README: new "Middleware backends: DDS and Zenoh" section documenting the
  mutually-exclusive `dds`/`zenoh` features, the Zenoh router requirement, a
  per-capability interop matrix, the type-hash limitation, and pointers to
  docs/zenoh_study and docs/decisions. Add a 0.10 changelog line.
- CI: the zenoh job now lints the zenoh example
  (`cargo clippy --no-default-features --features zenoh --examples -D warnings`),
  guarding both the example and the DDS-example gating.

Verified locally: `zenoh_demo` runs (sends and receives on /chatter, logs to
/rosout); DDS examples still build under the default features; clippy clean on
both backends; nightly rustfmt clean.

Closes #13

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
Round out the Zenoh Context configuration by honouring the two environment
variables `rmw_zenoh` uses, when no explicit `ContextOptions::zenoh_config`
is provided:

- `ZENOH_SESSION_CONFIG_URI`: path to a JSON5 Zenoh config file used as the
  base config (via `zenoh::Config::from_file`); otherwise the built-in peer
  default is the base.
- `ZENOH_CONFIG_OVERRIDE`: a `;`-separated list of `key=value` JSON5
  assignments applied on top (e.g.
  `mode="client";connect/endpoints=["tcp/localhost:7447"]`).

Malformed overrides and unreadable config files are surfaced as errors
rather than silently ignored. This replaces the E3 `TODO` in
`default_config` and lets a `ros2-client` node join a `zenohd`-based
`rmw_zenoh` deployment purely through the standard env vars.

A unit test covers the override parser (valid list applies and sets the
mode; empty entries ignored; an entry without `=` is rejected). 42 zenoh
lib tests pass; clippy (both backends) and nightly rustfmt are clean.

Closes #6

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
…iption

Complete the remaining E1 item (#4): a backend-neutral view of the ROS
graph on the Zenoh backend, replacing the RustDDS-specific `NodeEvent::DDS`
for graph observation.

- `GraphEvent` (EntityDeclared / EntityUndeclared) + `GraphEntity`
  (kind, node_name, name, type_name) — owned, backend-neutral types
  (ADR-0004). `EntityKind` is now re-exported at the crate root.
- `GraphCache` fans out these events to any number of subscribers (one
  unbounded channel each), mirroring the DDS backend's status senders, on
  every liveliness PUT/DELETE.
- `Context::graph_event_stream()` returns an event stream; `Context` and
  `Node` gain `wait_for_publisher(topic)` / `wait_for_subscription(topic)`
  futures that resolve on discovery (immediately if already present, and
  race-free: they subscribe before checking the current count).

Tests (Tier B): `emits_declared_and_undeclared_events` (cache-level, incl.
non-token keys producing no event) and `wait_for_publisher_resolves_on_
discovery` (end-to-end over loopback — a waiter on one context resolves
when a publisher appears on another). 44 zenoh lib tests pass; clippy (both
backends) and nightly rustfmt clean.

Refs #4

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
Complete the ROS 2 action surface on the Zenoh backend by adding the two
remaining sub-entities, mirroring rmw_zenoh / the DDS backend:

- `cancel_goal` service — `action_msgs/srv/CancelGoal` on
  `<action>/_action/cancel_goal` (the shared, non-action-namespaced type).
  Client: `cancel_goal(goal_id)` / `cancel_all_goals()`. Server:
  `try_receive_cancel()` / `respond_cancel(id, return_code, goals_canceling)`.
- `status` topic — `action_msgs/msg/GoalStatusArray` on
  `<action>/_action/status`. Client: `take_status()`. Server:
  `publish_status(status_list)`.

The `action_msgs` message types are backend-neutral and reused directly.
`ActionClient`/`ActionServer` and `Node::create_action_{client,server}` are
extended to wire the extra client/server + sub/pub.

Tier-B test `cancel_and_status_roundtrip`: the client sends a goal, observes
it reach `Executing` on `/status`, cancels it (asserting `return_code=None`
and the goal present in `goals_canceling`), then observes `Canceled` on
`/status`. 45 zenoh lib tests pass; clippy (both backends) and nightly
rustfmt clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
Implement the core of ADR-0007 item (c): a backend-neutral, from-scratch
port of rosidl's `calculate_type_hash`, so a ros2-client can compute the
same `RIHS01_…` hash a C++ peer embeds in its Zenoh keys.

- New `zenoh_backend::type_description`: `FieldType` (with the base id table
  + array/bounded-seq/unbounded-seq offset scheme), `Field`,
  `IndividualTypeDescription`, `TypeDescription`, and:
  - `hashable_json()` — the exact canonical JSON (struct-order keys, `", "`
    / `": "` separators, `default_value` stripped, referenced types sorted
    by name, `ensure_ascii` escaping);
  - `rihs01()` — `RIHS01_` + lowercase-hex SHA-256 of that JSON;
  - `service_type_description()` — the synthetic service composition
    (`request_message`/`response_message`/`event_message` + closure).
- Reproduces the published hashes of `std_msgs/msg/String` and
  `example_interfaces/srv/AddTwoInts` byte-exactly, and cross-checks them
  against the known-types table so the two cannot silently diverge.
- `sha2` added as an unconditional (pure-Rust) dependency, matching the
  `xxhash-rust` precedent of keeping wire-format spec modules testable on
  any build. Verified on both the dds and zenoh builds.

What remains for ADR-0007 (c) is the msggen *pipeline* — resolving each
generated type's transitive nested-type closure across packages and
emitting the computed hash as a constant — documented in ADR-0007 and the
`type_hash` module. Until then the send direction still uses the (now
verified) known-types table for un-generated types.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
claude and others added 3 commits July 8, 2026 14:51
Address the interop follow-up in two honest parts, since a real ROS 2 +
rmw_zenoh stack can't run in this repo's CI:

- `pub_sub_through_router`: an end-to-end test that stands up a real
  in-process Zenoh *router* and connects two client-mode `Context`s to it,
  exchanging a message on /chatter. This exercises the client→router→client
  topology (ADR-0009 / the rmw_zenoh deployment model) that the peer-mode
  loopback tests never touch.
- `docs/zenoh_study/interop_runbook.md`: a concrete Tier-C procedure for
  validating the Zenoh backend against real ROS 2 + rmw_zenoh + zenohd —
  topics (both directions), services, actions (incl. cancel/status),
  parameters, rosout, and discovery — each mapped to its C# acceptance
  criterion, with the env-var config (`ZENOH_CONFIG_OVERRIDE` /
  `ZENOH_SESSION_CONFIG_URI`) and the type-hash/distro caveats. README links
  it from the interop section.

49 zenoh lib tests pass; clippy (both backends) and nightly rustfmt clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP3yDMFQ3h1mDPAty2g5ZK
Native std_msgs scalar topics (Float64, Float32, Int32/64, UInt32/64, Bool) now
advertise a correct RIHS01 type hash on the send direction, computed from their
one-field descriptions via `type_description` (byte-exact, exactly as for
String), so C++ rmw_zenoh subscribers match them. Previously only String and
AddTwoInts were in the hash table; other scalars fell back to the all-zero
placeholder and did not match a C++ subscriber. Closes the common-scalar gap
ahead of full msggen (ADR-0007). Adds a drift-guard test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

Zenoh Support

2 participants