hivectl: add hive-wide restart verb (stop then start)

`hivectl restart [scope]` cycles the scoped containers — composes the
existing stop + start daemon ops client-side (reusing the merged Stop/Start
wire ops + global --socket), so no new wire/c0re surface. Same scope model
as stop/start (--agents/--agent/--ci/--forge/--gateway/--matrix), and
--graceful on the stop half. If the stop phase reports a failure the start
phase is skipped so a half-stopped hive isn't blindly started over.
Regenerated docs/tools/hivectl-cli.md.
This commit is contained in:
atlas 2026-06-19 12:04:35 +02:00
commit 31a4947aff
2 changed files with 47 additions and 0 deletions

View file

@ -133,6 +133,22 @@ enum Cmd {
#[command(flatten)]
scope: ScopeArgs,
},
/// Restart containers hive-wide — `stop` then `start` over the same
/// scope. Bare `hivectl restart` restarts **everything** (all sub-agents
/// plus the ci/forge/gateway/matrix infra containers); the same scope
/// flags as `stop`/`start` narrow it (`--agents`, `--ci`, `--forge`,
/// `--gateway`, `--matrix`, `--agent <name>`). If the stop phase reports
/// a failure the start phase is skipped so the operator can investigate.
/// Requires the hive-c0re daemon.
Restart {
#[command(flatten)]
scope: ScopeArgs,
/// Gracefully quiesce each agent on the stop half (see
/// `stop --graceful`). NOTE: not yet effective — falls through to a
/// hard stop until the per-agent quiesce lands.
#[arg(long)]
graceful: bool,
},
/// Emit the full CLI reference as `CommonMark` to stdout.
///
/// Hidden tooling command (not part of day-to-day operator admin):
@ -420,6 +436,7 @@ async fn main() -> Result<()> {
},
Cmd::Stop { scope, graceful } => stop(&socket, scope.to_scope(), graceful).await,
Cmd::Start { scope } => start(&socket, scope.to_scope()).await,
Cmd::Restart { scope, graceful } => restart(&socket, scope.to_scope(), graceful).await,
Cmd::Choom { name, fresh } => choom(&name, fresh),
Cmd::MarkdownDocs => {
print!("{}", clap_markdown::help_markdown::<Cli>());
@ -891,6 +908,16 @@ async fn start(socket: &Path, scope: hive_sh4re::LifecycleScope) -> Result<()> {
render_lifecycle(&resp, "started")
}
/// Restart = `stop` then `start` over the same scope, composed client-side
/// from the two daemon ops (no dedicated wire op). The stop phase honours
/// `--graceful`; if it reports a failure (`stop` returns `Err`) the `?`
/// short-circuits before the start phase, so a half-stopped hive isn't
/// blindly started over — the operator sees the stop errors and can recover.
async fn restart(socket: &Path, scope: hive_sh4re::LifecycleScope, graceful: bool) -> Result<()> {
stop(socket, scope.clone(), graceful).await?;
start(socket, scope).await
}
/// Render a hive-wide stop/start response: one `<verb>: <name>` line per
/// touched container, then surface any aggregated per-target failure as a
/// non-zero exit. `verb` is the past-tense word printed per item