Before: two bridges ran as anonymous OS processes. The only way to know whether one was alive was ps aux | grep bobit on the host, and the only way to know whether it was working was to watch the chat. If one died overnight, nothing anywhere recorded that it had.
After: each bridge is a row in the agent registry with a last_heartbeat that advances every 60 seconds, and a dead one is flipped to offline by the orchestrator within 180 seconds, on its own. Studio’s existing agents list renders it with no template change at all — the read path really was as type-agnostic as the audit claimed, which is the kind of prediction worth checking rather than assuming.
Liveness was moved ahead of identity deliberately: it is the one phase that delivers something before a supervisor exists. It adds no supervisor, starts and stops nothing, builds no UI, records no cost, and moves no flag into config — those are Phases 2 to 5. It writes rows a later phase will read, plus one that a running system reads immediately.
It needed three rows, not two. The plan said a bridge needs an Agent row and an API key. Reading the middleware found a third: without an active AgentSiteAssignment, every call returns 403 agent is not assigned to this site. That third row is also why this phase closed the untyped task-assignment paths in the same slice — the assignment is precisely what makes a bridge look assignable to code that never filters on type.
And that was not theoretical. sites.agents_enabled gates the agent UI but not the agent API. Measured: roughindustries has it off, so its bridge was shielded by accident; willartley has it on, so that bridge would have appeared in a live assign-agent dropdown. Guards that work by accident are not guards, and that is why the two exclusions shipped here rather than being deferred as belt-and-braces.