You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Two modules convert between the offboard policy wire and roboarm.command, and neither owns the boundary:
Site
Shape it handles
utils/serialization.py — _pack / _unpack
the __cmd__ envelope, via to_wire / from_wire
policy/remote.py — RemoteSession._as_command
a command served as a bare {'type': ..., ...} mapping
Both answer the same question — a served command becoming the typed object the drivers dispatch on — and splitting them costs twice.
A generic utility carries domain knowledge.utils/serialization.py opens by saying so itself:
This module currently knows about roboarm.command directly. If we accumulate more domain types that need wire treatment (gripper commands, observation packets, etc.), replace the inline dispatch with a generic registry / __to_wire__ protocol so utils stays domain-agnostic.
Nothing answers "where does the wire become a command?" The conversion has since sat in utils/serialization.py, then policy/codec.py, then policy/remote.py. Six separate review findings on #576 flagged comments in four modules, each trying to state this boundary's contract from wherever its author was standing — codec.py, remote.py, keys.py, and the tests. Each finding was right that its site neither states nor enforces the rule. The rule still has no site that does.
The shape that already exists next door
simulator/env_server/protocol.py owns the env-server boundary's wire codec in one module, deliberately positronic-free so a benchmark's interpreter can import it. The offboard boundary has no equivalent: offboard/ holds client.py and server.py, and its encoding lives in utils/.
Scope
One owner for the offboard wire's command conversion, accepting both shapes it already accepts
utils/serialization.py drops its roboarm import
offboard/client.py and offboard/server.py are the users
Both shapes stay supported. The bare mapping is what a partner endpoint sends today; #576 carries tests that pin it through a live socket, and those must keep passing.
Out of scope, and why
The env-server wire (Model env-server command types as an enum, parsed at the wire boundary #592). A different vocabulary on purpose: cartesian against cartesian_pos, joint_vel/dq against joint_delta/velocities, hold for None, no reset, and _wire_command raises on a CartesianDelta outside the env control frame rather than encoding it. Its far end must not import positronic, so it cannot share this code.
dataset/serializers.py::Serializers.robot_command. A storage schema, unfolding a command into .pose / .joints / .pose_delta columns. Datasets outlive protocols.
What has to be checked before starting
Moving command handling out of utils/serialization.py is safe only if the remote dataset wire (dataset/remote.py, dataset/remote_server/server.py) never carries a CommandType. Serializers.robot_command unfolds commands into arrays at record time, so a stored episode should hold vectors rather than command objects — inferred from the serializer, not verified end to end. This is load-bearing: the two wires share serialise/deserialise.
Separate finding, not part of this scope
policy/recording.py::_command_field_arrays calls roboarm_command.to_wire to name the columns it writes, so recorded column names derive from the network encoding. Renaming a wire field would silently change what a recording is keyed on, and datasets written either side of that change would stop matching. Worth its own issue if it survives a closer look.
Two modules convert between the offboard policy wire and
roboarm.command, and neither owns the boundary:utils/serialization.py—_pack/_unpack__cmd__envelope, viato_wire/from_wirepolicy/remote.py—RemoteSession._as_command{'type': ..., ...}mappingBoth answer the same question — a served command becoming the typed object the drivers dispatch on — and splitting them costs twice.
A generic utility carries domain knowledge.
utils/serialization.pyopens by saying so itself:Nothing answers "where does the wire become a command?" The conversion has since sat in
utils/serialization.py, thenpolicy/codec.py, thenpolicy/remote.py. Six separate review findings on #576 flagged comments in four modules, each trying to state this boundary's contract from wherever its author was standing —codec.py,remote.py,keys.py, and the tests. Each finding was right that its site neither states nor enforces the rule. The rule still has no site that does.The shape that already exists next door
simulator/env_server/protocol.pyowns the env-server boundary's wire codec in one module, deliberately positronic-free so a benchmark's interpreter can import it. The offboard boundary has no equivalent:offboard/holdsclient.pyandserver.py, and its encoding lives inutils/.Scope
utils/serialization.pydrops itsroboarmimportoffboard/client.pyandoffboard/server.pyare the usersBoth shapes stay supported. The bare mapping is what a partner endpoint sends today; #576 carries tests that pin it through a live socket, and those must keep passing.
Out of scope, and why
cartesianagainstcartesian_pos,joint_vel/dqagainstjoint_delta/velocities,holdforNone, noreset, and_wire_commandraises on aCartesianDeltaoutside the env control frame rather than encoding it. Its far end must not import positronic, so it cannot share this code.dataset/serializers.py::Serializers.robot_command. A storage schema, unfolding a command into.pose/.joints/.pose_deltacolumns. Datasets outlive protocols.What has to be checked before starting
Moving command handling out of
utils/serialization.pyis safe only if the remote dataset wire (dataset/remote.py,dataset/remote_server/server.py) never carries aCommandType.Serializers.robot_commandunfolds commands into arrays at record time, so a stored episode should hold vectors rather than command objects — inferred from the serializer, not verified end to end. This is load-bearing: the two wires shareserialise/deserialise.Separate finding, not part of this scope
policy/recording.py::_command_field_arrayscallsroboarm_command.to_wireto name the columns it writes, so recorded column names derive from the network encoding. Renaming a wire field would silently change what a recording is keyed on, and datasets written either side of that change would stop matching. Worth its own issue if it survives a closer look.