Payload Flow Contracts
A Flow[In, Out] declares its I/O contract in the type parameters: it consumes
one typed input port and returns one typed output payload, and both must be
@io-decorated. The runtime enforces this at class-definition time.
Declaring a non-@io type as a port raises immediately, so the contract can’t
drift silently:
That split is the whole design: standard spatial/perception/language payloads are the port currency; applied GoldenRetriever payloads travel as values carried inside steps, returned to callers, resolved through the registry, and exported via Hub.
One typed port in, one typed payload out
Section titled “One typed port in, one typed payload out”The controller stage from the boundary demo is a real Flow[PoseStamped, TwistStamped] — both ports are @io spatial types, so frame, time, and source
survive the handoff:
demo-robotics-typing-boundary runs the full camera → frame-normalization →
control chain as plain typed functions and then round-trips the command through
Arrow. The point is that the frame/source transition is in the payload, not
implied:
The pose enters in camera_color_optical_frame and leaves as a command in
base_link; the serialized round-trip recovers the same TwistStamped with its
frame intact.
Multiple inputs: tuple ports and qualified access
Section titled “Multiple inputs: tuple ports and qualified access”For more than one input, a Flow takes a tuple of ports. The shipped
DetectionGrounder is Flow[(ReferringExpression, DetectionBatch), GroundedPhrase] and reads each input by its type name:
When two ports share a field name, unqualified access is ambiguous by design.
demo-robotics-typing-contract demonstrates the rule on a composite of two
PoseStamped payloads plus a JointState: a unique field resolves unqualified,
a shared field (header) raises, and qualified access disambiguates before a
planner step consumes it.
Contract rules
Section titled “Contract rules”| Rule | Practical meaning | Failure avoided |
|---|---|---|
Ports are @io types. |
Use spatial/perception/language payloads as Flow ports; carry applied types as values. | FlowError at class definition. |
| Prefer typed payloads over dicts. | Type names become documentation, registry keys, and validation anchors. | Anonymous payloads only one example understands. |
| Keep field names stable. | Downstream Flows compose without adapter glue. | Silent breakage after renaming goal_pose to target. |
| Qualify shared fields. | Multiple poses/goals/statuses stay distinguishable. | Collisions between pose.header and base.header. |
| Validate at boundaries. | Normalize frame, time, source, and array lengths before reuse. | Bad payloads reaching planner/controller Flows. |
Runtime semantics for Flow, Pipeline, clocks, and sync live in the core Retriever docs. This page only covers the applied robot-payload contract built on top of them.
Source: examples/advanced/robotics_typing_standard/perception_to_control_boundary_demo.py,
compositional_contract_demo.py; tuple-port Flow in
examples/advanced/language_examples/common.py.
