fix(#1932): hivectl start restores only the previously-running agents

This commit is contained in:
damocles 2026-06-23 14:59:40 +02:00 committed by mara
commit 6516d4282e
2 changed files with 57 additions and 2 deletions

View file

@ -101,10 +101,32 @@ async fn dispatch(req: &HostRequest, coord: Arc<Coordinator>) -> HostResponse {
// graceful-stop queue, to re-expand it).
let agents = scoped_agents(scope).await?;
let infra = scoped_infra(scope);
// On a broad stop, remember which agents were actually
// running so a later broad `start` restores only those
// (not every configured container). A targeted `--agent`
// stop must not redefine the restore set.
if is_broad_scope(scope) {
let mut running = Vec::new();
for a in &agents {
if lifecycle::is_running(a).await {
running.push(a.clone());
}
}
coord.set_last_stopped_running(running);
}
handle_stop(&coord, &agents, &infra, *graceful).await?
}
HostRequest::Start { scope } => {
let agents = scoped_agents(scope).await?;
let mut agents = scoped_agents(scope).await?;
// A broad start restores only the set recorded at the
// last broad stop, if any. No record (cold "bring the
// hive up", or a daemon restart since the stop) → start
// all. Targeted `--agent` start is never filtered.
if is_broad_scope(scope)
&& let Some(prev) = coord.take_last_stopped_running()
{
agents.retain(|a| prev.contains(a));
}
let infra = scoped_infra(scope);
handle_start(&agents, &infra).await?
}
@ -328,10 +350,19 @@ async fn handle_start(agents: &[String], infra: &[InfraContainer]) -> Result<Hos
/// container (from `lifecycle::list`) when `agents` is set or the scope is
/// "everything", plus any explicit `agent_names`. Returns de-duplicated
/// logical names with the `h-` container prefix stripped.
/// A scope that targets *every* agent rather than an explicit
/// `--agent <name>` list: either the `agents` flag or a bare
/// "everything" scope. Broad scopes are the ones whose stop/start pair
/// drives the previously-running restore set (see `handle` Stop/Start
/// arms); a targeted `--agent` stop/start must not redefine it.
fn is_broad_scope(scope: &LifecycleScope) -> bool {
scope.agents || scope.is_everything()
}
async fn scoped_agents(scope: &LifecycleScope) -> Result<Vec<String>> {
use std::collections::BTreeSet;
let mut set: BTreeSet<String> = BTreeSet::new();
if scope.agents || scope.is_everything() {
if is_broad_scope(scope) {
for c in lifecycle::list().await? {
let logical = c
.strip_prefix(lifecycle::AGENT_PREFIX)