Skip to content

Carry domain types to the local/remote border, and serialize only there #638

Description

@vertix

The harness serializes every observation channel before anything reads it, so what a device produced —
a roboarm.State — reaches the policy stack as flat suffixed strings. Meaning then has to be recovered
from names.

Where this stands

Harness._build_obs calls each channel's serializer and flattens the result:

value = obs.serializer(value)
inputs.update({full: v for full, v in expand_suffixed(name, value) if v is not None})

So an arm's status arrives as the entry robot_state.status (or robot_state.{side}.status on a
bimanual rig), and StopOnFault — which runs in-process, on the rig, with the object right there —
finds it by matching that name:

def _is_robot_status(name: str) -> bool:
    return name.startswith(f'{keys.ROBOT_STATE}.') and name.endswith('.status')

Consequences:

  • A rig-side wrapper reads a string convention where it could read a type. The convention is spelled in
    more than one place and nothing checks the two agree.
  • The suffixes a serializer emits (.q, .status) and the canonical names in positronic/keys.py are
    the same contract written twice.
  • Everything downstream pays the flattening cost even when no wire is involved: an in-process pipeline
    serializes and never deserializes.

Decision

The harness passes domain objects. Serialization happens at the local/remote border and in the dataset
writer, and nowhere else. A wrapper that needs the arm's status reads it off the value with isinstance,
not off a name.

Consequences

  • eval.Observation loses its serializer: the value's domain type owns its policy- and dataset-side
    encoding. (The class already carries a TODO saying so.)
  • _is_robot_status in positronic/policy/wrappers.py goes, and StopOnFault reads State.status.
  • The offboard client serializes on the way out; a server-side stack keeps reading names, since the wire
    carries no types.
  • keys.ROBOT_STATUS and friends stay: they name recorded signals and wire entries, which is what they
    are for.

Relation to other work

Falls out of #637, which put the arm's status on the wire as data and left the name-matching behind.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions