pub async fn serve(opts: Opts) -> Result<()>Expand description
Serve the UI until Ctrl-C, finishing a run the loop has in flight.
The server itself owns no state, so nothing here is graceful for the HTTP side’s sake: the connections go with the dropped listener, which costs a phone one change-stream reconnection it was going to make anyway.
The signal branch is not optional now that the loop lives in this process.
daemon::serve_until listens for Ctrl-C itself, and a registered
handler is what stops the signal terminating the process - so without a
branch of our own, the first Ctrl-C after the operator started the loop
would stop the loop and leave magi web listening forever, unkillable
from the terminal it was started in.
What it waits for is the loop, not the sockets. A run in flight is
finished first, for the reason daemon::serve gives: killing the graph
mid-node leaves worktrees, branches and agent sessions behind and throws
away every agent call already paid for.
The server therefore runs on a task of its own rather than inside the
select!: an arm that resolves drops the futures the other arms were
polling, so serving the address from inside one would take the deck down
at the instant the handover began and keep it down for the whole park -
up to timeout_implement, an hour by default. See [hand_over], which
owns the order.