Skip to main content

Module upgrade

Module upgrade 

Source
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§

QuiesceOutcome
How quiesce_srv actually 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).
UpgradeError
Why run_migration_upgrade didn’t complete.

Constants§

GRACEFUL_QUIESCE_TIMEOUT
How long quiesce_srv waits 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 at SRV_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 migrate subprocess. 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-unimplemented install_update for 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).