One gateway per host is the only supported topology (#100896). This is the convergence
path that makes it true on an upgraded machine.
`gateway migrate --multiplex` is now defined by TOPOLOGY, not by the config flag: a host is
converged when no secondary profile owns a gateway process or a supervisor unit. A
half-migrated host (flag flipped, a unit left behind, a crash between the two) therefore
converges on the next run instead of reporting "already multiplexed" — that flag-only test
made the re-run a no-op on exactly the host that needed it. The manifest is the resume
record: present means UNFINISHED, so a confirmed convergence clears it and any manifest
found on disk is resumed from rather than refused.
Windows Scheduled Tasks (and the Startup-folder fallback `gateway install` writes when it
cannot register a task) are now detected and removed like any other unit; Windows used to
be a flat refusal with hand-migration instructions. s6 stays refused, because there the
per-profile gateways are slots the container's own boot registers — and that boot now
registers named slots DOWN unconditionally, which is the s6 leg of the convergence. It
used to read `gateway.multiplex_profiles`, so the UNSET default (on) booted the slots
anyway and the image shipped the opt-out topology by accident.
A running gateway is never SIGTERMed silently: the plan names every pid it will signal and
prints before anything is signalled, with a dry run that changes nothing.
`--standalone` is gone. Reinstalling per-profile services is not a supported target, so
there is no rollback command; the machinery survives only as the compensator inside a
single failed apply, because the one outcome worse than a per-profile fleet is a profile
with no gateway at all.
`gateway.multiplex_profiles: false` is retired as a topology opt-out: it still parses and
still carries the runtime mode every scoped code path reads, but it can no longer pin a
second gateway process — it resolves like an unset key, warns, and points at the migration.
The unset path is not optimistic (it refuses to multiplex while a real blocker holds), so a
host that genuinely cannot fold still comes up standalone and says why. The eager Desktop
activation stops reading it too: a stale `false` there made a multi-home host serve a
second profile with the LAUNCH profile's credentials.
The host gateway lock flips from observe-only to a refusal. `host_attach` already
attaches/rescans/refuses before anything binds, but it reads a RECORD published a moment
after the owner starts, so two gateways launched together can both see no owner. The lock
is the only atomic arbiter of that race. The refusal names the owner, prints the migrate
command, and exits 75 (EX_TEMPFAIL) so a supervisor RETRIES — never the parking 78 — and on
the retry the record exists and the attach path resolves it properly. `--force`, `--replace`
and an unopenable lock dir are not refusals.