diff --git a/hive-c0re/src/actions.rs b/hive-c0re/src/actions.rs index 55a65eae..6aba31fe 100644 --- a/hive-c0re/src/actions.rs +++ b/hive-c0re/src/actions.rs @@ -161,8 +161,8 @@ pub async fn run_approval_apply_commit( let approval = fetch_approval_for_worker(coord, approval_id, ApprovalKind::ApplyCommit)?; // Runtime dir creation is handled inside lifecycle::rebuild_no_meta's // spawn path (first-spawn) or is already present for rebuilds. - let agent_dir = Coordinator::agent_dir(&approval.agent); - let applied_dir = Coordinator::agent_applied_dir(&approval.agent); + let agent_dir = crate::paths::agent_runtime_dir(&approval.agent); + let applied_dir = crate::paths::applied_dir(&approval.agent); coord.set_queue_step(queue_entry_id, "apply commit"); let (result, terminal_tag, is_first_spawn) = run_apply_commit(coord, &approval, &agent_dir, &applied_dir, queue_entry_id).await; @@ -194,8 +194,8 @@ pub async fn run_approval_merge_config_pr( approval_id: i64, ) -> Result<()> { let approval = fetch_approval_for_worker(coord, approval_id, ApprovalKind::MergeConfigPr)?; - let agent_dir = Coordinator::agent_dir(&approval.agent); - let applied_dir = Coordinator::agent_applied_dir(&approval.agent); + let agent_dir = crate::paths::agent_runtime_dir(&approval.agent); + let applied_dir = crate::paths::applied_dir(&approval.agent); coord.set_queue_step(queue_entry_id, "merge config pr"); let (result, terminal_tag) = run_merge_config_pr(coord, &approval, &agent_dir, &applied_dir, queue_entry_id).await; @@ -911,7 +911,7 @@ pub async fn destroy(coord: &Arc, name: &str, purge: bool) -> Resul let guard = coord.transient_guard(name, TransientKind::Destroying); lifecycle::destroy(name).await?; coord.unregister_agent(name); - let runtime = Coordinator::agent_dir(name); + let runtime = crate::paths::agent_runtime_dir(name); if runtime.exists() { let _ = std::fs::remove_dir_all(&runtime); } @@ -924,8 +924,8 @@ pub async fn destroy(coord: &Arc, name: &str, purge: bool) -> Resul tracing::warn!(error = ?e, %name, "purge: delete state subvolume failed"); } for dir in [ - Coordinator::agent_state_root(name), - Coordinator::agent_applied_dir(name), + crate::paths::agent_state_dir(name), + crate::paths::applied_dir(name), ] { if dir.exists() && let Err(e) = std::fs::remove_dir_all(&dir) @@ -997,7 +997,7 @@ pub async fn deny(coord: &Coordinator, id: i64, note: Option<&str>) -> Result<() // the annotated body) the reason. Spawn approvals have no // commit to tag, so they fall through unannotated. if matches!(a.kind, ApprovalKind::ApplyCommit) { - let applied_dir = Coordinator::agent_applied_dir(&a.agent); + let applied_dir = crate::paths::applied_dir(&a.agent); let proposal_ref = format!("refs/tags/proposal/{id}"); if lifecycle::git_rev_parse(&applied_dir, &proposal_ref) .await diff --git a/hive-c0re/src/bin/hivectl.rs b/hive-c0re/src/bin/hivectl.rs index b393d92e..eef8d729 100644 --- a/hive-c0re/src/bin/hivectl.rs +++ b/hive-c0re/src/bin/hivectl.rs @@ -1048,7 +1048,7 @@ fn human_bytes(n: u64) -> String { /// "needs root" error fixes that first-run footgun, where running a /// privileged verb without sudo reported as a missing agent. fn agent_exists(name: &str) -> Result { - let root = Coordinator::agent_state_root(name); + let root = hive_c0re::paths::agent_state_dir(name); match root.try_exists() { Ok(found) => Ok(found), Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => bail!( diff --git a/hive-c0re/src/coordinator.rs b/hive-c0re/src/coordinator.rs index 931c725d..ab62c459 100644 --- a/hive-c0re/src/coordinator.rs +++ b/hive-c0re/src/coordinator.rs @@ -537,7 +537,7 @@ impl Coordinator { /// Assemble the per-agent filesystem paths for `name`. `agent_dir` /// is the runtime directory (`/run/hyperhive/agents/`), obtained - /// from `Coordinator::agent_dir(name)` (pure path) or from + /// from `crate::paths::agent_runtime_dir(name)` (pure path) or from /// `lifecycle::ensure_agent_runtime_dir(name)` when the dir must be /// created. All other paths are derived statically from `name`. #[must_use] @@ -545,7 +545,7 @@ impl Coordinator { AgentPaths { agent_dir, proposed_dir: Self::agent_proposed_dir(name), - applied_dir: Self::agent_applied_dir(name), + applied_dir: crate::paths::applied_dir(name), claude_dir: Self::agent_claude_dir(name), notes_dir: Self::agent_notes_dir(name), } @@ -1124,7 +1124,7 @@ impl Coordinator { // Idempotent: drop any existing listener so re-registration (e.g. on rebuild, // or after a hive-c0re restart cleared /run/hyperhive) gets a fresh socket. self.unregister_agent(name); - let agent_dir = Self::agent_dir(name); + let agent_dir = crate::paths::agent_runtime_dir(name); std::fs::create_dir_all(&agent_dir) .with_context(|| format!("create agent dir {}", agent_dir.display()))?; let socket_path = Self::socket_path(name); @@ -1432,25 +1432,14 @@ impl Coordinator { errors } - pub fn agent_dir(name: &str) -> PathBuf { - crate::paths::agent_runtime_dir(name) - } - pub fn socket_path(name: &str) -> PathBuf { - Self::agent_dir(name).join("mcp.sock") - } - - /// Ensure a runtime dir + (for sub-agents) per-agent socket exists. - /// - /// Per-agent state root (parent of `config/`, future `prompts/`, etc.). - pub fn agent_state_root(name: &str) -> PathBuf { - crate::paths::agent_state_dir(name) + crate::paths::agent_runtime_dir(name).join("mcp.sock") } /// Manager-editable proposed config repo. Bind-mounted into the manager /// container as `/agents//config/`. pub fn agent_proposed_dir(name: &str) -> PathBuf { - Self::agent_state_root(name).join("config") + crate::paths::agent_state_dir(name).join("config") } /// Per-agent Claude credentials dir. Bind-mounted RW into the agent @@ -1458,14 +1447,14 @@ impl Coordinator { /// destroy/recreate. Each agent owns its own token lineage — sharing /// would break on the first refresh-token rotation. pub fn agent_claude_dir(name: &str) -> PathBuf { - Self::agent_state_root(name).join("claude") + crate::paths::agent_state_dir(name).join("claude") } /// Per-agent durable knowledge dir. Bind-mounted RW into the agent /// container at `/agents/{name}/state`. Survives destroy/recreate. /// Agent-visible — claude is told to write long-lived notes here. pub fn agent_notes_dir(name: &str) -> PathBuf { - Self::agent_state_root(name).join("state") + crate::paths::agent_state_dir(name).join("state") } /// Per-agent harness-internal state dir. Bind-mounted RW into the @@ -1475,12 +1464,7 @@ impl Coordinator { /// from the agent-visible `state/` so claude's "my notes" view is /// uncluttered and the host vacuum has a clean sweep root. pub fn agent_harness_dir(name: &str) -> PathBuf { - Self::agent_state_root(name).join("harness") - } - - /// Authoritative applied config repo. Hive-c0re-only. - pub fn agent_applied_dir(name: &str) -> PathBuf { - crate::paths::applied_dir(name) + crate::paths::agent_state_dir(name).join("harness") } /// Enumerate names that have a persistent state dir under @@ -1514,7 +1498,7 @@ impl Coordinator { .into_iter() .filter(|n| { Self::agent_proposed_dir(n).join(".git").exists() - && !Self::agent_applied_dir(n).join(".git").exists() + && !crate::paths::applied_dir(n).join(".git").exists() }) .collect() } diff --git a/hive-c0re/src/dashboard/approvals.rs b/hive-c0re/src/dashboard/approvals.rs index 60f7be0f..9581b9a2 100644 --- a/hive-c0re/src/dashboard/approvals.rs +++ b/hive-c0re/src/dashboard/approvals.rs @@ -110,7 +110,7 @@ pub(super) fn gc_orphans(coord: &Coordinator, approvals: Vec) -> Vec String { - let applied = Coordinator::agent_applied_dir(agent); + let applied = crate::paths::applied_dir(agent); if !applied.join(".git").exists() { return format!("(no applied git repo at {})", applied.display()); } @@ -190,7 +190,7 @@ pub(super) async fn get_approval_diff( if !matches!(approval.kind, hive_sh4re::ApprovalKind::ApplyCommit) { return Err(error_problem("spawn approvals carry no commit to diff")); } - let applied = Coordinator::agent_applied_dir(&approval.agent); + let applied = crate::paths::applied_dir(&approval.agent); if !applied.join(".git").exists() { return Ok(plain_text(format!( "(no applied git repo at {})", diff --git a/hive-c0re/src/dashboard/tombstones.rs b/hive-c0re/src/dashboard/tombstones.rs index 8cd6c408..885f9676 100644 --- a/hive-c0re/src/dashboard/tombstones.rs +++ b/hive-c0re/src/dashboard/tombstones.rs @@ -47,7 +47,7 @@ pub(super) fn build_tombstone_views( .into_iter() .filter(|name| !live.contains(name.as_str())) .map(|name| { - let root = Coordinator::agent_state_root(&name); + let root = crate::paths::agent_state_dir(&name); let state_bytes = dir_size_bytes(&root); let last_seen = std::fs::metadata(&root) .and_then(|m| m.modified()) @@ -133,8 +133,8 @@ pub(super) async fn post_purge_tombstone( } let mut errors = Vec::new(); for dir in [ - Coordinator::agent_state_root(&name), - Coordinator::agent_applied_dir(&name), + crate::paths::agent_state_dir(&name), + crate::paths::applied_dir(&name), ] { if dir.exists() && let Err(e) = std::fs::remove_dir_all(&dir) diff --git a/hive-c0re/src/forge/pr_merge.rs b/hive-c0re/src/forge/pr_merge.rs index 74398dec..ca268140 100644 --- a/hive-c0re/src/forge/pr_merge.rs +++ b/hive-c0re/src/forge/pr_merge.rs @@ -7,8 +7,6 @@ use anyhow::Context; use forgejo_api::ForgejoError; use forgejo_api::structs::{MergePullRequestOption, MergePullRequestOptionDo}; -use crate::coordinator::Coordinator; - use super::{CONFIG_ORG, api, core_token, forge_git_url}; // --------------------------------------------------------------------------- @@ -133,7 +131,7 @@ pub async fn fetch_pr_head_into_applied(repo: &str, pr: u64) -> Result<(), Forge let token = core_token() .ok_or_else(|| ForgeMergeError::Other(anyhow::anyhow!("forge core token absent")))?; let url = forge_git_url(&token, repo); - let applied = Coordinator::agent_applied_dir(repo_agent_name(repo)); + let applied = crate::paths::applied_dir(repo_agent_name(repo)); let refspec = format!("refs/pull/{pr}/head"); let out = crate::lifecycle::git_command() .current_dir(&applied) @@ -165,7 +163,7 @@ pub async fn ff_push_to_main(repo: &str, sha: &str) -> Result<(), ForgeMergeErro let token = core_token() .ok_or_else(|| ForgeMergeError::Other(anyhow::anyhow!("forge core token absent")))?; let url = forge_git_url(&token, repo); - let applied = Coordinator::agent_applied_dir(repo_agent_name(repo)); + let applied = crate::paths::applied_dir(repo_agent_name(repo)); // Current `main` on the forge repo. let ls = crate::lifecycle::git_command() diff --git a/hive-c0re/src/forge/repos.rs b/hive-c0re/src/forge/repos.rs index f8aa5703..2bc5c186 100644 --- a/hive-c0re/src/forge/repos.rs +++ b/hive-c0re/src/forge/repos.rs @@ -360,7 +360,7 @@ pub async fn push_config(name: &str) -> Result<()> { let Some(token) = core_token() else { return Ok(()); }; - let dir = Coordinator::agent_applied_dir(name); + let dir = crate::paths::applied_dir(name); if !dir.join(".git").exists() { return Ok(()); } diff --git a/hive-c0re/src/job_queue/exec.rs b/hive-c0re/src/job_queue/exec.rs index 51b35358..a37b7202 100644 --- a/hive-c0re/src/job_queue/exec.rs +++ b/hive-c0re/src/job_queue/exec.rs @@ -100,7 +100,7 @@ async fn run_prebuild( // Prebuild runs while the agent is still up — the runtime dir and // MCP listener already exist. Use the pure path accessor; no need // to re-register the listener (event-driven: registered at start/create). - let agent_dir = Coordinator::agent_dir(name); + let agent_dir = crate::paths::agent_runtime_dir(name); let hive = coord.hive_env(); let paths = Coordinator::agent_paths(name, agent_dir); crate::lifecycle::prepare_rebuild_dirs(name, &paths).await?; @@ -134,7 +134,7 @@ async fn run_swap(coord: &Arc, claim: &Claim, ctx: &Ctx<'_>) -> Res let name = &claim.agent; // Swap runs on an already-existing (stopped) container — runtime dir // and listener were created earlier. Pure path accessor suffices. - let agent_dir = Coordinator::agent_dir(name); + let agent_dir = crate::paths::agent_runtime_dir(name); let hive = coord.hive_env(); let paths = Coordinator::agent_paths(name, agent_dir); let result = @@ -181,7 +181,7 @@ async fn run_swap(coord: &Arc, claim: &Claim, ctx: &Ctx<'_>) -> Res /// build+create — no prebuild needed). async fn run_create(coord: &Arc, claim: &Claim, ctx: &Ctx<'_>) -> Result { let name = &claim.agent; - let agent_dir = Coordinator::agent_dir(name); + let agent_dir = crate::paths::agent_runtime_dir(name); let hive = coord.hive_env(); let paths = Coordinator::agent_paths(name, agent_dir); ctx.step("nixos-container create"); @@ -255,7 +255,7 @@ async fn run_reconcile( // exists and writes the nspawn/resource-limits drop-ins. // The returned StartableAgent token is the only way to call // start_with_fallback — omitting this becomes a compile error. - let agent_dir = Coordinator::agent_dir(name); + let agent_dir = crate::paths::agent_runtime_dir(name); let hive = coord.hive_env(); let paths = Coordinator::agent_paths(name, agent_dir); let token = crate::lifecycle::converge_start_preamble(name, &hive, &paths).await?; @@ -346,7 +346,7 @@ async fn run_write_dropin(coord: &Arc, claim: &Claim) -> Result Vec { } async fn migrate_applied_repo(name: &str) -> Result<()> { - let dir = Coordinator::agent_applied_dir(name); + let dir = crate::paths::applied_dir(name); if !dir.join(".git").exists() { return Ok(()); } diff --git a/hive-c0re/src/server.rs b/hive-c0re/src/server.rs index bf0b3779..f43da1db 100644 --- a/hive-c0re/src/server.rs +++ b/hive-c0re/src/server.rs @@ -202,7 +202,7 @@ async fn dispatch(req: &HostRequest, coord: Arc) -> HostResponse { /// registration and notifying the manager on failure. async fn handle_spawn(coord: &Arc, name: &str) -> Result { tracing::info!(%name, "spawn"); - let agent_dir = Coordinator::agent_dir(name); + let agent_dir = crate::paths::agent_runtime_dir(name); let hive = coord.hive_env(); let paths = Coordinator::agent_paths(name, agent_dir); // lifecycle::spawn creates the runtime dir internally before start. diff --git a/hive-c0re/src/socket_server/config_approvals.rs b/hive-c0re/src/socket_server/config_approvals.rs index cabfc994..492322b9 100644 --- a/hive-c0re/src/socket_server/config_approvals.rs +++ b/hive-c0re/src/socket_server/config_approvals.rs @@ -196,7 +196,7 @@ pub(crate) async fn submit_apply_commit( ) -> anyhow::Result<(i64, String)> { validate_commit_ref(commit_ref)?; let proposed_dir = crate::coordinator::Coordinator::agent_proposed_dir(agent); - let applied_dir = crate::coordinator::Coordinator::agent_applied_dir(agent); + let applied_dir = crate::paths::applied_dir(agent); if !proposed_dir.exists() { anyhow::bail!( "proposed repo missing for agent '{agent}' (expected at {})", diff --git a/hive-c0re/src/socket_server/mod.rs b/hive-c0re/src/socket_server/mod.rs index af5e6254..e88e6de8 100644 --- a/hive-c0re/src/socket_server/mod.rs +++ b/hive-c0re/src/socket_server/mod.rs @@ -99,7 +99,7 @@ pub fn start(agent: &str, socket_path: &Path, coord: Arc) -> Result /// standard per-agent runtime dir + socket, with no dedicated helpers. pub fn start_manager(coord: Arc) -> Result<()> { use std::os::unix::fs::PermissionsExt as _; - let dir = Coordinator::agent_dir(crate::lifecycle::MANAGER_NAME); + let dir = crate::paths::agent_runtime_dir(crate::lifecycle::MANAGER_NAME); std::fs::create_dir_all(&dir) .with_context(|| format!("create manager dir {}", dir.display()))?; let socket = Coordinator::socket_path(crate::lifecycle::MANAGER_NAME); @@ -1026,7 +1026,7 @@ pub(crate) fn handle_send( }; } if resolved != hive_sh4re::OPERATOR_RECIPIENT { - let state_root = crate::coordinator::Coordinator::agent_state_root(&resolved); + let state_root = crate::paths::agent_state_dir(&resolved); if !state_root.exists() { return AgentResponse::Err { message: format!( diff --git a/hive-c0re/src/workers/auto_update.rs b/hive-c0re/src/workers/auto_update.rs index cc4e1a91..3b2c280d 100644 --- a/hive-c0re/src/workers/auto_update.rs +++ b/hive-c0re/src/workers/auto_update.rs @@ -111,7 +111,7 @@ pub async fn ensure_root_agent(coord: &Arc) -> Result<()> { // (no applied flake on disk) we must rebuild — otherwise it's // running whatever the host-declarative config was at create // time, with a wrong systemd unit and port. - let applied_flake = Coordinator::agent_applied_dir(MANAGER_NAME).join("flake.nix"); + let applied_flake = crate::paths::applied_dir(MANAGER_NAME).join("flake.nix"); if !applied_flake.exists() && current_rev.is_some() { tracing::warn!( "manager container exists but no applied flake — forcing rebuild to migrate" @@ -151,7 +151,7 @@ pub async fn ensure_root_agent(coord: &Arc) -> Result<()> { tracing::info!("manager container missing — spawning"); // lifecycle::spawn creates the runtime dir internally; no manual // ensure_agent_runtime_dir needed here. - let runtime = Coordinator::agent_dir(MANAGER_NAME); + let runtime = crate::paths::agent_runtime_dir(MANAGER_NAME); let hive = coord.hive_env(); let paths = Coordinator::agent_paths(MANAGER_NAME, runtime); lifecycle::spawn(MANAGER_NAME, &hive, &paths).await?;