Skip to content

Implement initial server catalog Websocket flow - #3

Merged
arteeh merged 3 commits into
mainfrom
codex/implement-features-from-todo.md
Jul 9, 2026
Merged

Implement initial server catalog Websocket flow#3
arteeh merged 3 commits into
mainfrom
codex/implement-features-from-todo.md

Conversation

@arteeh

@arteeh arteeh commented Jul 2, 2026

Copy link
Copy Markdown
Member

Motivation

  • Provide the first working server-side WebSocket control-plane implementation so pilot clients and robot drivers can exchange v1 MessagePack envelopes and the server can maintain a live Robot Catalog.
  • Establish in-memory driver bookkeeping, watchdogs, and duplicate-robotId handling as a foundation before implementing acquisition/session lifecycle state machines.

Description

  • Added a MessagePack WebSocket control-plane server implementation in server/ito/app.py that accepts binary frames, requires connection.hello, performs role-based routing, and returns standard protocol result envelopes.
  • Implemented in-memory DriverRecord and ConnectionState to track driver connections, robot.status handling, disconnect handling, status freshness evaluation, and duplicate robotId detection that marks affected robots Unavailable.
  • Implemented pilot catalog.get handling with optional includeUnavailable filtering and catalog entry assembly from driver records.
  • Added websockets to server/requirements.txt, created unit tests tests/test_server_app.py, and updated docs/todo.md to mark catalog/status tasks complete and note remaining work on outbound request timeouts and session lifecycle.

Testing

  • Installed Python deps with python -m pip install -r server/requirements.txt and the install succeeded.
  • Ran the test suite with python -m pytest -q and all tests passed (11 passed).
  • Compiled modules with python -m compileall server tests which completed successfully.

This is a natural stopping point because the next TODOs cover acquisition and session-start/request timeouts which introduce significant session-state machinery best implemented and reviewed on top of this WebSocket/catalog foundation.


Codex Task

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 232f0eea53

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/ito/app.py
Comment on lines +172 to +174
if record.connection is not None and record.connection is not state:
record.duplicate = True
record.connection = None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear duplicate state after conflicts disconnect

When a driver reconnect overlaps with the old WebSocket, this branch sets record.duplicate = True and drops the tracked connection, but no disconnect path or later hello ever clears that flag. Because _handle_robot_status ignores duplicate records, the same robotId can never become available again after the conflicting connections go away, so a transient reconnect race leaves the robot unavailable until the server restarts.

Useful? React with 👍 / 👎.

Comment thread server/ito/app.py
return
state.role = role
state.robot_id = robot_id
record = self.drivers.setdefault(robot_id, DriverRecord(robot_id=robot_id))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Wait for robot.status before cataloging drivers

Creating a DriverRecord during connection.hello makes a driver visible in catalog.get before it has ever sent a robot.status; the catalog entry then falls back to the robotId as the name and Droid as the type. When a driver has just connected, is stuck before its first status heartbeat, or the server has just restarted, pilots can see placeholder/incorrect robot entries even though the catalog is supposed to be built from Driver Status Reports and not show a robot until it reports.

Useful? React with 👍 / 👎.

Comment thread server/ito/app.py
return
if role == ROLE_PILOT_CLIENT:
state.role = role
state.session_id = payload.get("sessionId")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate pilot resume session IDs before storing them

If a pilot hello includes a non-string payload.sessionId (for example from corrupt local storage or an incompatible client), this assignment stores it and the subsequent hello response tries to include it as the envelope sessionId, causing make_envelope validation to raise instead of returning a failed connection.hello.result. That turns a bad resume claim into a dropped WebSocket/handler error rather than the displayable rejection described by the protocol.

Useful? React with 👍 / 👎.

@arteeh
arteeh merged commit d1ee483 into main Jul 9, 2026
1 check passed
@arteeh
arteeh deleted the codex/implement-features-from-todo.md branch July 9, 2026 20:04

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 94d87a93a6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/ito/app.py
Comment on lines +180 to +183
if role == ROLE_PILOT_CLIENT:
state.role = role
state.session_id = payload.get("sessionId")
await self._send_result(state, TYPE_CONNECTION_HELLO_RESULT, envelope["messageId"], result_ok({"protocolVersion": PROTOCOL_VERSION, "role": role, "sessionResumed": False} if state.session_id else {"protocolVersion": PROTOCOL_VERSION, "role": role}))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject unresumable pilot session hellos

When a pilot reconnects with a sessionId, this path accepts the hello and returns ok: true with sessionResumed: False even though this server has no session state and therefore cannot resume any claimed session. The protocol says a claimed session that cannot be accepted should produce a failed connection.hello.result, so after a server restart or stale local storage the client gets an accepted connection without a displayable rejection reason instead of being told the session is gone.

Useful? React with 👍 / 👎.

Comment thread server/ito/app.py
Comment on lines +202 to +203
detail = payload.get("availabilityDetail")
record.availability_detail = detail if isinstance(detail, dict) else None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate availabilityDetail before cataloging

When a driver reports an availabilityDetail map that is not a valid Display Reason, such as {"code": 404} or {"detail": "offline"}, this assignment preserves it and catalog.get later returns a catalog entry that violates the protocol's Display Reason shape. That malformed catalog payload can break pilot-side localization/display code even though the bad status report could be ignored or normalized here.

Useful? React with 👍 / 👎.

Comment thread server/ito/app.py
Comment on lines +134 to +136
except ProtocolError as exc:
LOGGER.warning("Rejecting invalid Ito envelope: %s", exc)
await self._send_error(state, TYPE_CONNECTION_HELLO_RESULT, None, "protocol.invalid_message")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return correlated version mismatch errors

When a peer sends a parseable connection.hello with the wrong protocolVersion, unpack_envelope raises before the server keeps the request messageId, and this handler replies with a generic protocol.invalid_message whose replyToMessageId is None. The protocol requires version rejections to be failed connection.hello.result responses, so mismatched clients cannot correlate the displayable failure to their hello request and may treat it as a timeout instead of a clear version-mismatch rejection.

Useful? React with 👍 / 👎.

Comment thread server/ito/app.py
Comment on lines +169 to +170
state.role = role
state.robot_id = robot_id

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject re-hello before changing identity

If a connected driver sends another connection.hello with a different robotId or role, these assignments overwrite the connection state without clearing the previous DriverRecord that still points at this same websocket. A buggy retry on one socket can therefore leave the old robot cataloged against a connection now owned by a different identity, and later disconnect cleanup only touches the new identity.

Useful? React with 👍 / 👎.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant