You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PATCH /v1/agents/:name with {"status":"active"} on a stale agent returns "offline" in the response body and simultaneously broadcasts agent.status.active to realtime subscribers. Same request, same instant, two different answers about the same agent.
Originally surfaced by cubic on #306 and confirmed independently while reviewing that PR.
Mechanism
updateAgent writes the status column but never renews last_seen:
effectiveAgentStatus derives from last_seen, so for a row whose last_seen is older than AGENT_LIVENESS_TTL_MS it returns offline no matter what was just written to the column.
The realtime fanout on the same request reads the requested value instead:
So the HTTP caller is told offline while every subscriber is told active, and the stored column now says active while the derived view says offline.
Why it is worth a separate issue
This is the same "two fields, one name" hazard that made the presence area expensive to reason about (#312, and the identity guard in #306). It is not, however, the same severity: after #306 the reclaim guard reads last_seen, so updateAgent no longer moves an identity boundary. Before that change, this endpoint was a write path into the reclaim guard — setting status: 'active' re-tightened a row while proving nothing about liveness. That part is now closed; the reporting divergence is not.
Options, not a recommendation
Worth deciding rather than patching the symptom:
have the fanout use effectiveAgentStatus(updated) so both surfaces agree — cheapest, keeps the write meaningless for a stale row
have an explicit status: 'active' write renew last_seen, on the grounds that an operator asserting liveness is an activity signal — but this makes an unauthenticated-of-the-agent PATCH able to assert an agent's liveness, which is exactly the coupling fix(engine): restore agent presence lifecycle #306 removed. Probably wrong for that reason.
reject status writes that contradict observed activity, and return 409
drop caller-supplied status from this endpoint entirely, on the grounds that presence is server-observed and not something a caller should be able to assert
Not urgent
No identity or delivery consequence after #306. It is a correctness and observability defect: monitoring and any consumer reacting to agent.status.* will disagree with the API for up to one TTL window.
Summary
PATCH /v1/agents/:namewith{"status":"active"}on a stale agent returns"offline"in the response body and simultaneously broadcastsagent.status.activeto realtime subscribers. Same request, same instant, two different answers about the same agent.Originally surfaced by cubic on #306 and confirmed independently while reviewing that PR.
Mechanism
updateAgentwrites thestatuscolumn but never renewslast_seen:effectiveAgentStatusderives fromlast_seen, so for a row whoselast_seenis older thanAGENT_LIVENESS_TTL_MSit returnsofflineno matter what was just written to the column.The realtime fanout on the same request reads the requested value instead:
So the HTTP caller is told
offlinewhile every subscriber is toldactive, and the stored column now saysactivewhile the derived view saysoffline.Why it is worth a separate issue
This is the same "two fields, one name" hazard that made the presence area expensive to reason about (#312, and the identity guard in #306). It is not, however, the same severity: after #306 the reclaim guard reads
last_seen, soupdateAgentno longer moves an identity boundary. Before that change, this endpoint was a write path into the reclaim guard — settingstatus: 'active're-tightened a row while proving nothing about liveness. That part is now closed; the reporting divergence is not.Options, not a recommendation
Worth deciding rather than patching the symptom:
effectiveAgentStatus(updated)so both surfaces agree — cheapest, keeps the write meaningless for a stale rowstatus: 'active'write renewlast_seen, on the grounds that an operator asserting liveness is an activity signal — but this makes an unauthenticated-of-the-agent PATCH able to assert an agent's liveness, which is exactly the coupling fix(engine): restore agent presence lifecycle #306 removed. Probably wrong for that reason.statuswrites that contradict observed activity, and return409statusfrom this endpoint entirely, on the grounds that presence is server-observed and not something a caller should be able to assertNot urgent
No identity or delivery consequence after #306. It is a correctness and observability defect: monitoring and any consumer reacting to
agent.status.*will disagree with the API for up to one TTL window.