Expand description
Phase 1 of the fast-startup redesign
(docs/specs/SPEC_FAST_STARTUP_UPGRADE_OWNS_MIGRATIONS_AND_UPDATES_2026_09_15.md,
tracked by issue #3258) — the primitives a “quiesce srv, run pending
migrations, come back” upgrade action needs, built and tested in
isolation ahead of wiring them into a live caller.
WHY THIS IS SPLIT FROM ITS EVENTUAL CALLER. The spec’s §4.3 sequence
(maintenance state → quiesce → snapshot → migrate → relaunch) has two
halves with very different risk profiles: the actual quiesce/migrate
logic below is a self-contained, thoroughly-testable async function; the
“suspend the live supervisor loop’s respawn/teardown handling” half
means editing supervisor/windows.rs’s and supervisor/unix.rs’s
tokio::select! state machines — ~700- and ~1200-line functions this
repo has no way to exercise end-to-end outside a real multi-process
launcher run (see the I1-I6 isolation invariants in
docs/specs/SPEC_MULTI_INSTANCE_ISOLATION_HARDENING_2026_06_03.md;
this is exactly the surface they exist to protect). §4.2 also hasn’t landed yet, which is what
decides WHEN this runs at all — an upgrade-only srv mode most likely
means srv is never handed to the normal supervised loop as a
respawn-on-exit child in the first place, so the “suspend the loop”
problem may look completely different once that exists. Wiring this into
today’s loops now would be guessing at an integration shape that phase 2
gets to actually decide. This module is deliberately the callable,
tested half; the calling half is follow-up work once §4.2 lands.
(quiesce_srv itself is live: both supervisors stop srv with it on
quit, after their loops have ended, so no respawn handling is involved.)
Constraint 2 from the spec — “migrations only run with no live srv
holding that data dir” (341faa981’s data-loss incident) — is what
quiesce_srv exists to guarantee: it returns only once the OS has
genuinely reaped the process (Child::wait()’s own contract), replacing
crates/cef/src/commands/backend.rs’s dev-mode-only 300ms sleep
heuristic with a real wait for real exit.
No snapshot step lives here on purpose: agentmux-srv migrate’s own
apply_pending (shared with the in-process daemon path) already takes
its own backup under the same cross-process migration lock before
applying anything (crates/srv/src/migrations/runner.rs’s
backup_stores call) — a launcher-side snapshot would be redundant with,
not a replacement for, that one. The spec’s §4.3 step 3 was written
assuming the launcher would re-implement what bootstrap/stores.rs does for
the in-process path; going through the CLI subcommand instead means that
safety property already exists on this path.
Enums§
- Quiesce
Outcome - How
quiesce_srvactually stopped the process — logged, and useful in tests, but callers don’t need to branch on it: either variant means the same thing (the process is genuinely gone). - Upgrade
Error - Why
run_migration_upgradedidn’t complete.
Constants§
- GRACEFUL_
QUIESCE_ TIMEOUT - How long
quiesce_srvwaits for srv to exit on its own before escalating to a hard kill: long enough for srv to close every agent (agent_teardown::app_exit, capped atSRV_APP_EXIT_CAP).
Functions§
- quiesce_
srv - Stop srv and return only once it has genuinely exited — never a fixed
sleep. The launcher’s one way of stopping srv: on every quit (both
supervisors) and before a migration (
run_migration_upgrade). - run_
migration_ upgrade - Quiesce the running srv, then run pending migrations via the
agentmux-srv migratesubprocess. Does NOT relaunch srv afterward and does NOT install a staged app update first — both are the caller’s responsibility once one exists (§4.2’s boot gate for the former;app-update-check.md’s still-unimplementedinstall_updatefor the latter, per the spec’s §5.4 ordering — a staged update must be installed BEFORE this runs, so its migrations are the ones that end up applied).