phux docs
Decisions

0111 — How a front restore is judged

Amends ADR-0110. A record names its session by id and creation time, so a graceful server upgrade keeps it.

stable document
Full source summary

Amends ADR-0110. A record names its session by id and creation time, so a graceful server upgrade keeps it. A front record whose host's connection fails once before it is shown waits for the backoff redial, a lister; a second failure drops it. This Mac's first projection at launch leaves a restored peer's selected tab in front.

Status: Accepted Date: 2026-09-11

Context

ADR-0110 re-shows at launch the one remembered host whose tab was the front window’s selected tab, once its own list still carries the session and the front window has a measured size. Three cases were left open.

A record named its session by id and a hash of HELLO_OK.server_id (decisions 1 and 3). Every server exec changes that id, a graceful upgrade included, so an upgrade dropped the record although the session, its id and its creation time all survived it (the upgrade blob carries them).

A host whose first connection failed dropped its record (ADR-0110 decision 5), so a host that was merely slow to accept its first dial came back listing for the whole launch.

The order of the first projections was not settled. If This Mac’s first projection landed after the restored peer’s and took the selection, the peer would be hidden, return to listing, and the restore would be lost.

Rules A and B of ADR-0110 hold throughout: a coordinator that is not displaying never attaches and never holds a viewport, and input, edits and metadata writes go only to the coordinator that minted the ref.

Decision

  1. One retry. A pending front record survives one failed connection of its host, whether it fails before the host lists or after it lists but before a frame measured the front window. What that connection listed is forgotten. The backoff redial, which like every automatic redial only lists, is judged when it lists, by the same checks as the first connection: the session still listed, not empty, and a measured front window. A second failure before the record is shown drops it. Any choice the user makes meanwhile cancels it, as ADR-0110 decision 5 does for the first connection.
  2. The launch race. This Mac’s first projection at launch carries no remembered selection, because a Phux launch reads no topology state file. Landing after the restored peer’s first projection, it places its tabs beside the peer’s and leaves the peer’s tab selected, so the peer stays shown. A choice that lands with it, such as a navigation to one of This Mac’s terminals, takes the tab. The peer is then hidden and returns to listing as any hidden peer does: its tabs leave, and only its own connection restarts, as a lister that sends no ATTACH.
  3. The key. A record keeps the session’s creation time from the session list, shown=<session>,@<unix seconds>,<window>,<front>, in place of the server hash. It applies only on the coordinator its target line names, and only when that coordinator’s list carries the same session id with the same creation time. A line written before this carries the server hash in that field; it still reads, is judged by the hash as before, and so is dropped by any restart. Capture writes the hash only when the list does not give the creation time.

Why

Retrying once covers a host that was still starting, or whose network was not up yet, at launch. It costs nothing under rule A: the redial is a lister’s, so nothing attaches until its list judges the record, exactly as on the first connection. Retrying only once bounds how late a restore can take the front window. The first redial comes about a second after the failure; a host that returns later comes back listing rather than pulling the front window away from whatever the user is doing by then.

The race needs no new rule. A peer’s first projection takes the selection, and a later projection takes it only with a choice behind it. Rule A is kept by the existing settlePeers, which returns any peer whose tabs are not on screen to listing.

A graceful upgrade keeps every session with its id and creation time; a cold restart reissues ids from 1 to sessions created anew. The pair tells the two apart with no wire change, from a field the session list already carries.

Tradeoffs

  • A host that fails twice stays listing for that launch, even if it comes back a minute later.
  • A restore after a failure appears a second or more after launch.
  • Between the failure and the redial the record stays front in the .remote file, so a quit then keeps it for the next launch.
  • Creation times are whole seconds. A cold restart that gives the id to a session created in the same second as the remembered one would show that session instead. Rules A and B still hold then: it is displayed, on its own coordinator.
  • A build that reads only server-hash lines treats a file with the new line as malformed and forgets its hosts: one Connect to Host after a downgrade.

Alternatives

Retry on every redial for the whole launch. A host that returns a minute later would take the front window from whatever the user is doing, which a restore should never do.

Retry on a faster timer of its own. A second timer per slot for one case, where the backoff redial already exists and already only lists.

Make This Mac’s projection yield the selection to a restored peer. A special case in the shared workspace for a state that already resolves correctly without one.

Key on the ADR-0109 instance token. It survives exactly an upgrade, but neither HELLO_OK nor the session list carries it; a client reads it only from a bound spawn, so this would need wire surface.

View exact source