Report the robot's actual status, and only when it changes - #113
Merged
Conversation
leandropineda
approved these changes
Aug 28, 2026
_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
force-pushed
the
consult-online-status-on-connect
branch
from
August 28, 2026 14:21
357ed64 to
be46488
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_connectpublished 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 noget_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'sbasics.jsonStatesetsupdateStampfor 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_statevia 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.jssendsget_stateonly 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_stateis 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(), mirroringregister_command_callback.RobotSessionPool.get_session()builds and connects a session in one step, so a caller has no window to callRobotSession.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:The factory's existing
build_callbackwrapper is deliberately not reused: it discards the return value, and this callback is read for its result.Notes for review
_on_connect, in addition to_handle_get_state(same thread). It must stay cheap and non-blocking.Falsenow 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.get_state, and on disconnect.get_stateround trip — and hand the health-check result topublish_status()each iteration so a robot going offline is reported once, at the moment it happens.Testing
False→ nothing published; callback raising → onlinepublish_status: skips an unchanged status, publishes on changeget_stateis answered even when the status is unchanged160 passed, flake8 and black clean.test_models.py::TestRobotSessionModel::test_model_creationfails identically on a cleanmainin my environment (localuse_sslenv override), unrelated.🤖 Generated with Claude Code