diff --git a/hive-c0re/src/server.rs b/hive-c0re/src/server.rs index ceb46b3e..2e2a041a 100644 --- a/hive-c0re/src/server.rs +++ b/hive-c0re/src/server.rs @@ -94,8 +94,20 @@ async fn dispatch(req: &HostRequest, coord: Arc) -> HostResponse { HostResponse::success() } HostRequest::RestartAll => handle_restart_all().await?, - HostRequest::Stop { scope, graceful } => handle_stop(scope, *graceful).await?, - HostRequest::Start { scope } => handle_start(scope).await?, + HostRequest::Stop { scope, graceful } => { + // Resolve the scope to explicit container names at the entry + // point, then operate on names — never pass the bare "all + // agents" flag deeper (it'd force every consumer, incl. the + // graceful-stop queue, to re-expand it). + let agents = scoped_agents(scope).await?; + let infra = scoped_infra(scope); + handle_stop(&agents, &infra, *graceful).await? + } + HostRequest::Start { scope } => { + let agents = scoped_agents(scope).await?; + let infra = scoped_infra(scope); + handle_start(&agents, &infra).await? + } HostRequest::Destroy { name, purge } => { actions::destroy(&coord, name, *purge).await?; HostResponse::success() @@ -202,24 +214,25 @@ async fn handle_restart_all() -> Result { } } -/// Stop the containers a [`LifecycleScope`] selects (`hivectl stop`): the -/// scoped sub-agents, then the scoped infra containers. Agents go down -/// before infra so they're not mid-request against a forge/matrix that's -/// already gone. Per-target failures are aggregated rather than aborting on -/// the first error, mirroring `handle_restart_all`. -async fn handle_stop(scope: &LifecycleScope, graceful: bool) -> Result { - tracing::info!(?scope, graceful, "stop"); +/// Stop the given `agents` (resolved logical names) then `infra` containers +/// (`hivectl stop`). Agents go down before infra so they're not mid-request +/// against a forge/matrix that's already gone. Per-target failures are +/// aggregated rather than aborting on the first error, mirroring +/// `handle_restart_all`. Callers resolve the [`LifecycleScope`] to these +/// explicit name lists up front — this never sees the "all" flag. +async fn handle_stop(agents: &[String], infra: &[&str], graceful: bool) -> Result { + tracing::info!(?agents, ?infra, graceful, "stop"); let mut ok_items: Vec = Vec::new(); let mut errors: Vec = Vec::new(); - for agent in scoped_agents(scope).await? { + for agent in agents { // TODO(graceful agent stop): when `graceful`, run the per-agent // quiesce (turn-end → drain → reject new messages) before the kill. // The flag is threaded through the wire now; the quiesce itself // lands with the graceful-agent-stop work. let _ = graceful; - match lifecycle::kill(&agent).await { - Ok(()) => ok_items.push(agent), + match lifecycle::kill(agent).await { + Ok(()) => ok_items.push(agent.clone()), Err(e) => { tracing::warn!(%agent, error = ?e, "stop: agent kill failed"); errors.push(format!("{agent}: {e:#}")); @@ -227,7 +240,7 @@ async fn handle_stop(scope: &LifecycleScope, graceful: bool) -> Result ok_items.push(container.to_owned()), Err(e) => { @@ -240,15 +253,16 @@ async fn handle_stop(scope: &LifecycleScope, graceful: bool) -> Result Result { - tracing::info!(?scope, "start"); +/// Start the given `infra` containers then `agents` (`hivectl start`) — the +/// inverse of [`handle_stop`]. Infra comes up before agents so the agents +/// find forge/matrix/gateway ready. Per-target failures aggregated. Callers +/// resolve the [`LifecycleScope`] to these explicit name lists up front. +async fn handle_start(agents: &[String], infra: &[&str]) -> Result { + tracing::info!(?agents, ?infra, "start"); let mut ok_items: Vec = Vec::new(); let mut errors: Vec = Vec::new(); - for container in scoped_infra(scope) { + for &container in infra { match crate::priv_client::control_infra_container(container, InfraAction::Start).await { Ok(()) => ok_items.push(container.to_owned()), Err(e) => { @@ -258,9 +272,9 @@ async fn handle_start(scope: &LifecycleScope) -> Result { } } - for agent in scoped_agents(scope).await? { - match lifecycle::start(&agent).await { - Ok(()) => ok_items.push(agent), + for agent in agents { + match lifecycle::start(agent).await { + Ok(()) => ok_items.push(agent.clone()), Err(e) => { tracing::warn!(%agent, error = ?e, "start: agent start failed"); errors.push(format!("{agent}: {e:#}"));