Two correctness defects from the deep-dive review.
B. register_agent left a stale in-memory "online" entry when the DB write
failed. The except block rolled back the session but kept the
ConnectedAgent in _agents, so the gateway still emitted auth_ok and
command routing targeted an agent with no backing AgentSession row
(online/offline desync). register_agent now returns Optional[str]: on DB
failure it removes its own in-memory entry (guarded on identity so a
concurrent reconnect isn't clobbered) and returns None. Both callers —
the WS gateway (on_auth) and the poll /connect endpoint — now emit
auth_fail / HTTP 500 instead of reporting a connected agent. A stale
'online' server row with no in-memory agent is self-corrected by the
heartbeat reaper.
C. send_command swallowed the final command-record DB update on the
timeout/result path: a failed commit left the row stuck in 'running'
with no log line, making it invisible. Now logs the exception (the row
is still left for a future sweep, but it's now diagnosable).
Adds a regression test that register_agent returns None and rolls back the
in-memory registration when the session write fails.
Full backend suite: 89 passed, 12 skipped.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>