fix(hu): resolve the message schema before subscribe returns - #311
Merged
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
Resolves schemas synchronously for WASM subscriptions, adds local .msg fallback, and improves runtime-typed example support.
Changes:
- Propagates schema failures before
subscribe()returns and pauses epoch accounting during host waits. - Adds hash-checked local schema fallback and enables type-description services in publishing examples.
- Improves diagnostics, regression tests, and Axum route compatibility.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
crates/hiroz/src/node.rs |
Documents type-description opt-in. |
crates/hiroz/src/lifecycle/node.rs |
Adds lifecycle builder pass-through. |
crates/hiroz/src/dynamic/type_info.rs |
Publicizes DDS type normalization. |
crates/hiroz/src/dynamic/mod.rs |
Re-exports type normalization. |
crates/hiroz/src/context.rs |
Initializes lifecycle option. |
crates/hiroz/examples/z_pubsub.rs |
Enables schema service and renames nodes. |
crates/hiroz/examples/z_custom_message.rs |
Enables schema service. |
crates/hiroz/examples/z_cache/talker.rs |
Enables schema service. |
crates/hiroz/examples/twist_pub.rs |
Enables schema service. |
crates/hiroz/examples/shm_pointcloud2.rs |
Enables schema service. |
crates/hiroz/examples/lifecycle/talker.rs |
Uses lifecycle pass-through. |
crates/hiroz/examples/laser_scan.rs |
Enables schema service. |
crates/hiroz/examples/demo_nodes/talker.rs |
Enables schema service. |
crates/hiroz-union/src/plugin/wasm/mod.rs |
Adds epoch suspension guard. |
crates/hiroz-union/src/plugin/wasm/host/ros.rs |
Resolves schemas synchronously with fallback. |
crates/hiroz-union/src/modes/web.rs |
Fixes Axum wildcard routing. |
crates/hiroz-union/src/core/logger.rs |
Includes hu in default filters. |
crates/hiroz-union/plugins/hu-meter/src/lib.rs |
Updates subscription behavior and status output. |
crates/hiroz-tests/tests/hu_meter.rs |
Tests unresolved-schema failure reporting. |
Suppressed comments (3)
crates/hiroz-union/src/plugin/wasm/host/ros.rs:38
- Only inspecting
.first()can miss a usable type. Graph ordering is not guaranteed, so when the first publisher hastype_info: Noneand a later publisher advertises the type, the fallback incorrectly reports that no endpoint advertises one. Scan all returned entities for the first endpoint withSome(type_info).
.get_entities_by_topic(kind, topic)
.first()
.and_then(|ent| match ent.as_ref() {
Entity::Endpoint(ep) => ep.type_info.clone(),
_ => None,
crates/hiroz-union/src/plugin/wasm/host/ros.rs:91
- The graph stores qualified topic names, but this fallback queries it with the caller's raw topic.
create_dyn_sub_auto("chatter", ...)correctly qualifies to/chatter; after discovery fails, this lookup searches forchatter, misses the live publisher, and prevents the local.msgfallback. Qualify the topic using the node namespace/name before querying the graph while still passing the original topic to the subscriber builder.
let Some(ti) = live_topic_type_info(graph, topic) else {
crates/hiroz-union/src/plugin/wasm/host/ros.rs:566
call_rawhas the same process-wide suspension issue: the guest controlstimeout_msup to about 49 days, and this guard disables epoch interruption for all stores for that entire wait. Cap the wait with a host-defined maximum or account for blocking time only on the calling store rather than pausing the shared ticker.
let _epoch = super::super::HostBlockGuard::enter();
replies.recv()
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| /// against a hand-built `Graph` without a wasmtime store. | ||
| fn live_topic_type_info(graph: &Graph, topic: &str) -> Option<hiroz_protocol::TypeInfo> { | ||
| use hiroz_protocol::{EndpointKind, Entity}; | ||
| [EndpointKind::Publisher, EndpointKind::Subscription] |
| // path uses (`encode_yaml_to_cdr`). The type *name* still comes | ||
| // from the graph; only the schema body comes from disk. | ||
| tracing::debug!("WASM plugin: schema discovery failed for {topic}: {e}"); | ||
| match dyn_sub_from_local_msg(&node, &self.engine.graph, &topic) { |
YuanYuYuan
force-pushed
the
fix/hu-schema-discovery
branch
from
August 21, 2026 10:42
030a7dd to
237914d
Compare
This was referenced Aug 21, 2026
YuanYuYuan
force-pushed
the
fix/hu-schema-discovery
branch
from
August 21, 2026 12:17
237914d to
d3b55c7
Compare
YuanYuYuan
force-pushed
the
feat/hiroz-type-desc-optin
branch
from
August 21, 2026 12:17
542c04b to
49d9162
Compare
YuanYuYuan
force-pushed
the
fix/hu-schema-discovery
branch
from
August 21, 2026 13:27
d3b55c7 to
bd2615d
Compare
YuanYuYuan
force-pushed
the
feat/hiroz-type-desc-optin
branch
from
August 21, 2026 13:27
49d9162 to
52d29dc
Compare
YuanYuYuan
force-pushed
the
fix/hu-schema-discovery
branch
from
August 21, 2026 14:12
bd2615d to
862d9ed
Compare
YuanYuYuan
force-pushed
the
feat/hiroz-type-desc-optin
branch
from
August 21, 2026 14:12
52d29dc to
207339b
Compare
The host resolved the schema on a spawned task, after `subscribe()` had already returned Ok. The plugin's failure branch could never fire, and the dropped channel sender read as an idle topic: `hu meter echo` on a topic it could not decode printed nothing and exited 0, which is indistinguishable from a topic with no traffic. Resolution now happens before the resource is minted, and the rep is allocated only once the subscription exists. When discovery fails -- which it does against any node built without the type description service -- the host falls back to a `.msg` on HIROZ_MSG_PATH, taking the type *name* from the graph, because `subscribe` carries only a topic. The disk fallback refuses a schema whose hash disagrees with what the topic advertises: CDR is positional, so a skewed schema yields plausible, wrong values rather than a decode error. "Advertises no hash" is a third state, not a mismatch -- a Humble node or any peer built without type hashing cannot be verified either way, and refusing there broke the cross-distro case the fallback exists for. Decode failures on the stream are now reported, first loudly and then at debug, for the same reason: a stream that decodes to nothing looks idle. hz and bw keep working on a topic whose type cannot be resolved. Their numbers come from a raw counting subscriber that never decodes, so the typed subscription they used to hold becomes best-effort and moves out of the mode, held only to keep them visible in the ROS graph. Fixes #297. Fixes #298. Fixes #299.
YuanYuYuan
force-pushed
the
fix/hu-schema-discovery
branch
from
August 21, 2026 15:08
862d9ed to
1040021
Compare
This was referenced Aug 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Last slice of 5, splitting the original #311. #317, #318, #319 and #320 are merged. This one now targets
main.Fixes #297. Fixes #298. Fixes #299.
What this does
The plugin host resolves a message schema before
subscribe()returns. The subscribe path also gains a fallback to a local.msgfile.What fails without this
The host resolved the schema on a spawned task.
subscribe()had already returnedOkby then. The plugin's failure branch could therefore never run. The dropped channel sender looked like an idle topic.hu meter echo /chatterhu meter delayResolution now happens before the host mints the resource. The host allocates the rep only after the subscription exists.
Discovery fails against any node that lacks the type description service. The host then falls back to a
.msgfile onHIROZ_MSG_PATH. The type name still comes from the graph, becausesubscribecarries only a topic.The fallback refuses a mismatched schema, and accepts an unverifiable one
CDR is positional. A skewed schema therefore yields plausible but wrong field values, not a decode error. The fallback compares the local hash against the advertised hash.
The third row matters most. A Humble node advertises nothing. Any peer built without type hashing also advertises nothing. An earlier version treated that as a mismatch, which broke every cross-distro case. Those cases are the main reason to have a disk fallback.
Compare hash values, never rendered strings.
hiroz_protocol's renderer is gated onno-type-hash. Under that feature it returns one constant for every value, so a string comparison passes on anything. The diagnostic renders through an ungated formatter for the same reason.Why the guest did not report the failure before
wasmtime gives each guest an epoch budget in wall clock. A blocking host call spends that budget while the guest does not run. A 5 s discovery therefore trapped the guest on return, before it could run the error branch that the host had just given it. A reported failure became a hang.
#319 fixed that. It is merged, so
HostBlockGuardsuspends the epoch ticker across this wait.Breaking changes
hu meter echo <topic>on a topic with no resolvable schema|| trueguards. A silent success was indistinguishable from an idle topic.hzandbwstill work on a topic whose type does not resolve. Their numbers come from a raw counting subscriber that never decodes. The typed subscription they used to hold is now best-effort, and it moves out of the mode. The host keeps it only so thathzandbwstay visible in the ROS graph.hu meter echonow writessubscribed to <topic>; waiting for messagesto stderr. A subscribe that succeeds and then prints nothing looks the same as a broken one.What this does not reach
#300 records the one case. A publisher may advertise no type at all. The fallback needs a type name, and the graph is its only source. That case needs an addition to the WIT interface.