diff --git a/hive-c0re/src/auto_update.rs b/hive-c0re/src/auto_update.rs index 87341af7..5355d20e 100644 --- a/hive-c0re/src/auto_update.rs +++ b/hive-c0re/src/auto_update.rs @@ -66,7 +66,7 @@ pub fn agent_config_pending(name: &str, deployed_sha: Option<&str>) -> bool { /// `queue_entry_id` is `Some(id)` when the rebuild was dispatched from /// the `rebuild_queue` worker (lets the function annotate its phase via /// `coord.set_queue_step`) and `None` when called directly (e.g. the -/// manager-migration nudge in `ensure_manager`). +/// root-agent migration nudge in `ensure_root_agent`). pub async fn rebuild_agent( coord: &Arc, name: &str, @@ -156,10 +156,13 @@ pub async fn rebuild_agent( } /// Auto-create the manager container on startup if it isn't already there. -/// hive-c0re manages `ruth` end-to-end: operators no -/// longer declare `containers.h-ruth` in their host NixOS config. Bypasses -/// the approval queue — manager is required infrastructure. Idempotent. -pub async fn ensure_manager(coord: &Arc) -> Result<()> { +/// hive-c0re manages the manager end-to-end: operators no longer declare +/// `containers.h-ruth` in their host NixOS config. Bypasses the approval +/// queue — manager is required infrastructure. Idempotent. +/// +/// (Renamed from `ensure_manager` to align with the "root agent" terminology +/// used in docs and issues; behaviour is identical.) +pub async fn ensure_root_agent(coord: &Arc) -> Result<()> { let existing = lifecycle::list().await.unwrap_or_default(); let current_rev = current_flake_rev(&coord.hyperhive_flake); if existing diff --git a/hive-c0re/src/coordinator.rs b/hive-c0re/src/coordinator.rs index d07e38dc..bb9bd98a 100644 --- a/hive-c0re/src/coordinator.rs +++ b/hive-c0re/src/coordinator.rs @@ -23,7 +23,6 @@ use crate::operator_questions::OperatorQuestions; const DASHBOARD_CHANNEL: usize = 256; const AGENT_RUNTIME_ROOT: &str = "/run/hyperhive/agents"; -const MANAGER_RUNTIME_ROOT: &str = "/run/hyperhive/manager"; /// Manager-editable per-agent config repos. Bind-mounted RW into the manager /// container as `/agents//`. Hive-c0re only writes to these on first /// spawn (initial commit); after that it's manager-only. @@ -857,12 +856,15 @@ impl Coordinator { Self::agent_dir(name).join("mcp.sock") } + /// Runtime dir for the manager. Uses the same per-agent subdir layout as + /// sub-agents — the manager is just another agent under + /// `AGENT_RUNTIME_ROOT` with its own subdirectory. pub fn manager_dir() -> PathBuf { - PathBuf::from(MANAGER_RUNTIME_ROOT) + Self::agent_dir(crate::lifecycle::MANAGER_NAME) } pub fn manager_socket_path() -> PathBuf { - Self::manager_dir().join("mcp.sock") + Self::socket_path(crate::lifecycle::MANAGER_NAME) } /// Ensure a runtime dir + (for sub-agents) per-agent socket exists. For @@ -872,7 +874,7 @@ impl Coordinator { /// `/run/hive/mcp.sock` bind that ends up in `set_nspawn_flags`. pub fn ensure_runtime(self: &Arc, name: &str) -> Result { if name == crate::lifecycle::MANAGER_NAME { - let dir = Self::manager_dir(); + let dir = Self::agent_dir(name); std::fs::create_dir_all(&dir) .with_context(|| format!("create manager dir {}", dir.display()))?; return Ok(dir); diff --git a/hive-c0re/src/crash_watch.rs b/hive-c0re/src/crash_watch.rs index ae3ce18e..6a7d89dc 100644 --- a/hive-c0re/src/crash_watch.rs +++ b/hive-c0re/src/crash_watch.rs @@ -33,13 +33,10 @@ pub fn spawn(coord: Arc) { let mut current_logged_in = HashSet::new(); let mut sub_agents: Vec = Vec::new(); for c in &raw { - let logical = if c == MANAGER_NAME { - MANAGER_NAME.to_owned() - } else if let Some(n) = c.strip_prefix(AGENT_PREFIX) { - n.to_owned() - } else { + let Some(logical) = c.strip_prefix(AGENT_PREFIX) else { continue; }; + let logical = logical.to_owned(); if logical != MANAGER_NAME { sub_agents.push(logical.clone()); } diff --git a/hive-c0re/src/main.rs b/hive-c0re/src/main.rs index a41cb326..8bbf0850 100644 --- a/hive-c0re/src/main.rs +++ b/hive-c0re/src/main.rs @@ -222,12 +222,12 @@ async fn cmd_serve( if let Err(e) = migrate::run(&coord).await { tracing::warn!(error = ?e, "startup migration failed"); } - // Auto-create the manager container if it isn't there yet. Block + // Auto-create the root agent container if it isn't there yet. Block // on this — without root the system has no manager harness. // Failures are logged but allowed: a broken auto-spawn shouldn't // make the dashboard unreachable for debugging. - if let Err(e) = auto_update::ensure_manager(&coord).await { - tracing::warn!(error = ?e, "auto-spawn manager failed"); + if let Err(e) = auto_update::ensure_root_agent(&coord).await { + tracing::warn!(error = ?e, "auto-spawn root agent failed"); } // Auto-update in the background — don't block service start. // Sub-agent rebuilds can take tens of seconds; we want the admin diff --git a/hive-c0re/src/manager_server.rs b/hive-c0re/src/manager_server.rs index 8a4ccfc1..39bba5d0 100644 --- a/hive-c0re/src/manager_server.rs +++ b/hive-c0re/src/manager_server.rs @@ -189,7 +189,7 @@ async fn dispatch(req: &ManagerRequest, coord: &Arc) -> ManagerResp let id = match coord .approvals .submit_kind( - hive_sh4re::MANAGER_AGENT, + MANAGER_AGENT, hive_sh4re::ApprovalKind::UpdateMetaInputs, &commit_ref, description.as_deref(), @@ -206,7 +206,7 @@ async fn dispatch(req: &ManagerRequest, coord: &Arc) -> ManagerResp tracing::info!(%id, %label, "update_meta_inputs approval queued"); coord.emit_approval_added( id, - hive_sh4re::MANAGER_AGENT, + MANAGER_AGENT, "update_meta_inputs", None, None, @@ -215,10 +215,10 @@ async fn dispatch(req: &ManagerRequest, coord: &Arc) -> ManagerResp ManagerResponse::Ok } ManagerRequest::RequestSchedulePrompt(payload) => { - handle_request_schedule_prompt(coord, hive_sh4re::MANAGER_AGENT, payload) + handle_request_schedule_prompt(coord, MANAGER_AGENT, payload) } ManagerRequest::CancelSchedule { id, targets } => { - handle_cancel_schedule(coord, hive_sh4re::MANAGER_AGENT, *id, targets.as_deref()) + handle_cancel_schedule(coord, MANAGER_AGENT, *id, targets.as_deref()) } ManagerRequest::EditSchedule { id, @@ -230,7 +230,7 @@ async fn dispatch(req: &ManagerRequest, coord: &Arc) -> ManagerResp targets_remove, } => handle_edit_schedule( coord, - hive_sh4re::MANAGER_AGENT, + MANAGER_AGENT, *id, body.clone(), description.clone(), @@ -248,7 +248,7 @@ async fn dispatch(req: &ManagerRequest, coord: &Arc) -> ManagerResp }, }, ManagerRequest::FireScheduleNow { id } => { - handle_fire_schedule_now(coord, hive_sh4re::MANAGER_AGENT, *id).await + handle_fire_schedule_now(coord, MANAGER_AGENT, *id).await } ManagerRequest::GetLogs { agent, lines } => { let n = lines.unwrap_or(50); diff --git a/hive-c0re/src/rebuild_queue.rs b/hive-c0re/src/rebuild_queue.rs index 0dd306de..a5238454 100644 --- a/hive-c0re/src/rebuild_queue.rs +++ b/hive-c0re/src/rebuild_queue.rs @@ -781,12 +781,8 @@ pub async fn meta_update_cascade_agents(inputs: &[String]) -> Vec { .unwrap_or_default() .into_iter() .filter_map(|c| { - if c == crate::lifecycle::MANAGER_NAME { - Some(crate::lifecycle::MANAGER_NAME.to_owned()) - } else { - c.strip_prefix(crate::lifecycle::AGENT_PREFIX) - .map(str::to_owned) - } + c.strip_prefix(crate::lifecycle::AGENT_PREFIX) + .map(str::to_owned) }) .collect() } else { diff --git a/hive-c0re/src/reminder_scheduler.rs b/hive-c0re/src/reminder_scheduler.rs index 58a02d25..c213882b 100644 --- a/hive-c0re/src/reminder_scheduler.rs +++ b/hive-c0re/src/reminder_scheduler.rs @@ -168,17 +168,12 @@ pub fn write_payload(agent: &str, host_path: &Path, message: &str) -> Result<(), } /// Container-visible state prefix the caller's `file_path` must live -/// under. Every agent sees its state at `/agents//state/` +/// under. Every agent sees its state at `/agents//state/` /// (see `lifecycle::set_nspawn_flags`). Auto-file paths use the same -/// prefix so the round-trip is symmetric. The manager logical name -/// maps to its container name (`root`) per `lifecycle::MANAGER_NAME`. +/// prefix so the round-trip is symmetric. #[must_use] pub fn container_state_prefix(agent: &str) -> String { - if agent == hive_sh4re::MANAGER_AGENT { - format!("/agents/{}/state/", crate::lifecycle::MANAGER_NAME) - } else { - format!("/agents/{agent}/state/") - } + format!("/agents/{agent}/state/") } /// Map an agent-visible container path to the matching host path, diff --git a/hive-c0re/src/scheduled_prompts_worker.rs b/hive-c0re/src/scheduled_prompts_worker.rs index 03440cef..d532c01e 100644 --- a/hive-c0re/src/scheduled_prompts_worker.rs +++ b/hive-c0re/src/scheduled_prompts_worker.rs @@ -190,6 +190,8 @@ fn known_agents(_coord: &Coordinator) -> std::collections::HashSet { // safe (we're not in a `current_thread` runtime). use std::collections::HashSet; let mut out: HashSet = HashSet::new(); + // Manager is always a scheduled-prompt target (fail-safe: include + // it even if `list()` fails so prompts to the manager never silently drop). out.insert(hive_sh4re::MANAGER_AGENT.to_owned()); let containers = tokio::task::block_in_place(|| { tokio::runtime::Handle::current().block_on(crate::lifecycle::list()) @@ -199,9 +201,9 @@ fn known_agents(_coord: &Coordinator) -> std::collections::HashSet { for raw in list { if let Some(name) = raw.strip_prefix(crate::lifecycle::AGENT_PREFIX) { out.insert(name.to_owned()); - } else if raw == crate::lifecycle::MANAGER_NAME { - out.insert(hive_sh4re::MANAGER_AGENT.to_owned()); } + // Note: the old `else if raw == MANAGER_NAME` branch was dead code; + // the manager container uses the h- prefix like all other agents. } } Err(e) => { @@ -376,9 +378,9 @@ async fn known_agents_async() -> std::collections::HashSet { for raw in list { if let Some(name) = raw.strip_prefix(crate::lifecycle::AGENT_PREFIX) { out.insert(name.to_owned()); - } else if raw == crate::lifecycle::MANAGER_NAME { - out.insert(hive_sh4re::MANAGER_AGENT.to_owned()); } + // Note: the old `else if raw == MANAGER_NAME` branch was dead code; + // the manager container uses the h- prefix like all other agents. } } Err(e) => {