Skip to content

Report the robot's actual status, and only when it changes - #113

Merged
b-Tomas merged 1 commit into
mainfrom
consult-online-status-on-connect
Aug 28, 2026
Merged

Report the robot's actual status, and only when it changes#113
b-Tomas merged 1 commit into
mainfrom
consult-online-status-on-connect

Conversation

@b-Tomas

@b-Tomas b-Tomas commented Aug 27, 2026

Copy link
Copy Markdown
Member

Problem

Two ways the agent moves a robot's offline timestamp when nothing about the robot changed.

1. Connecting claims the robot is online. _on_connect published an unconditional online status (robot.py:622). A connected session means the agent is up, not the robot. So starting an agent for an unreachable robot ends that robot's offline period, and nothing corrects it: with the robot marked online, InOrbit has no discrepancy to resolve, sends no get_state, and the online status callback that knows better is never consulted.

2. Every status message restamps. InOrbit stamps the robot's last-status time on each status message it receives (ingest's basics.js onState sets updateStamp for any non-retained state message), and that stamp is what the UI shows as how long a robot has been offline. So re-reporting a status the robot is already in keeps pushing that timestamp forward — on every reconnect, and on every agent shutdown.

Change

Take the connect-time status from the online status callback, sharing the resolution with _handle_get_state via a new _get_online_status(). The default-to-online fallback for a missing or failing callback is unchanged, so a session with no callback behaves exactly as before, and a callback that raises reports online rather than silently marking a robot offline.

When that status is offline, publish nothing at all. The previous session already reported the robot offline — gracefully through disconnect(), or through the MQTT will — so the message says nothing new and would only move the timestamp. disconnect() skips its offline message on the same grounds when the robot was already reported offline.

New public publish_status(online), which publishes only on a change. A caller that evaluates robot health on a loop can hand its result over on every iteration and get exactly one message per real transition. This matters more than it looks: publishing offline is the only way to report a robot offline while the session stays connected. InOrbit asks for state only when it already has the robot offline (system.js sends get_state only for a robot it believes offline), and the agent auto-echoes its pings (_on_message_send_echo) for as long as the process is alive.

get_state is still always answered — the de-duplication covers only statuses the robot volunteers, never a reply to something InOrbit asked for.

RobotSessionFactory.set_online_status_callback(), mirroring register_command_callback. RobotSessionPool.get_session() builds and connects a session in one step, so a caller has no window to call RobotSession.set_online_status_callback() before the first connect — the callback would only ever affect later reconnections. Registering on the factory attaches it at build time. It takes the robot id, since one factory serves a whole fleet:

factory.set_online_status_callback(lambda robot_id: fleet_manager.is_online(robot_id))

The factory's existing build_callback wrapper is deliberately not reused: it discards the return value, and this callback is read for its result.

Notes for review

  • The callback now runs on the paho network loop thread during _on_connect, in addition to _handle_get_state (same thread). It must stay cheap and non-blocking.
  • A callback stuck at False now sticks harder. Before, a robot with a broken check still got a connect-time online window until InOrbit timed it out. Raising still falls back to online — lying does not.
  • Callers that register no callback are unaffected on connect, on get_state, and on disconnect.
  • The MQTT will is unchanged and still restamps on an ungraceful drop. It is broker-side and fires exactly when the agent could not speak for itself, so there is nothing to de-duplicate.
  • Companion change in connector-sdk (Stop publishing system stats for offline robots inorbit-connector-python#91): stop publishing system stats for robots reported offline — that is what stops the per-loop get_state round trip — and hand the health-check result to publish_status() each iteration so a robot going offline is reported once, at the moment it happens.

Testing

  • Connect: no callback → online; callback False → nothing published; callback raising → online
  • publish_status: skips an unchanged status, publishes on change
  • Disconnect: silent when the robot was already offline, reports offline when it was online
  • get_state is answered even when the status is unchanged
  • Factory: the callback applies on the first connect and receives the robot id; non-callable ignored
  • 160 passed, flake8 and black clean. test_models.py::TestRobotSessionModel::test_model_creation fails identically on a clean main in my environment (local use_ssl env override), unrelated.

🤖 Generated with Claude Code

@b-Tomas b-Tomas changed the title Report the robot's actual status when a session connects Report the robot's actual status, and only when it changes Aug 27, 2026
Comment thread inorbit_edge/robot.py Outdated
_on_connect published an unconditional "online" status message, so a robot was
marked online in InOrbit whenever its MQTT session came up -- whether or not the
robot itself was reachable. Restarting a connector for a dead robot therefore
ended its offline period, and nothing corrected it: with the robot marked
online there is no discrepancy for InOrbit to resolve via get_state.

Take the status from the online status callback instead, sharing the resolution
(and its default-to-online fallback for a missing or failing callback) with
_handle_get_state.

That callback is only useful on connect if it is registered before connect(),
and a RobotSessionPool connects a session as soon as it builds it, leaving no
window to call RobotSession.set_online_status_callback() in time. Add
RobotSessionFactory.set_online_status_callback(), mirroring
register_command_callback, so the callback is attached at build time and covers
the first connection.

Add publish_status(), which publishes only when the status changed, so a caller
that evaluates the robot's health on a loop can hand the result over on every
iteration and produce one message per transition. This is the only way to report
a robot offline while its session stays connected: InOrbit asks for state only
when it already has the robot offline, and the session answers InOrbit's pings
for as long as the process is alive. disconnect() goes through it too, so ending
a session does not re-report a robot that was already offline.

Connecting still reports the status directly, without that de-duplication: the
will may have been published while the session was down, so InOrbit's view of
the robot is unknown at that point. get_state is likewise always answered --
InOrbit asked, so it gets an answer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@b-Tomas
b-Tomas force-pushed the consult-online-status-on-connect branch from 357ed64 to be46488 Compare August 28, 2026 14:21
@b-Tomas
b-Tomas merged commit f255ac3 into main Aug 28, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants