refactor(#2285): drop coordinator 1:1 path accessors, callers use paths:: directly

This commit is contained in:
damocles 2026-07-10 20:18:27 +02:00 committed by mara
commit cfb84b420a
13 changed files with 38 additions and 56 deletions

View file

@ -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<Coordinator>, 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<Coordinator>, 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

View file

@ -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<bool> {
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!(

View file

@ -537,7 +537,7 @@ impl Coordinator {
/// Assemble the per-agent filesystem paths for `name`. `agent_dir`
/// is the runtime directory (`/run/hyperhive/agents/<name>`), 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/<name>/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()
}

View file

@ -110,7 +110,7 @@ pub(super) fn gc_orphans(coord: &Coordinator, approvals: Vec<Approval>) -> Vec<A
/// dashboard event (instead of forcing the dashboard to wait a
/// `/api/state` cycle to see the diff for newly-queued approvals).
pub(crate) async fn approval_diff(agent: &str, approval_id: i64) -> 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 {})",

View file

@ -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)

View file

@ -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()

View file

@ -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(());
}

View file

@ -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<Coordinator>, 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<Coordinator>, claim: &Claim, ctx: &Ctx<'_>) -> Res
/// build+create — no prebuild needed).
async fn run_create(coord: &Arc<Coordinator>, claim: &Claim, ctx: &Ctx<'_>) -> Result<NodeOutput> {
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<Coordinator>, claim: &Claim) -> Result<Nod
// write_dropins only needs the path value to build AgentPaths; the
// dir doesn't need to exist at this point (created by ensure_agent_runtime_dir
// on the upstream Prebuild/Start node).
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::write_dropins(name, &hive, &paths).await?;

View file

@ -267,7 +267,7 @@ async fn enumerate_agents() -> Vec<String> {
}
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(());
}

View file

@ -202,7 +202,7 @@ async fn dispatch(req: &HostRequest, coord: Arc<Coordinator>) -> HostResponse {
/// registration and notifying the manager on failure.
async fn handle_spawn(coord: &Arc<Coordinator>, name: &str) -> Result<HostResponse> {
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.

View file

@ -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 {})",

View file

@ -99,7 +99,7 @@ pub fn start(agent: &str, socket_path: &Path, coord: Arc<Coordinator>) -> Result
/// standard per-agent runtime dir + socket, with no dedicated helpers.
pub fn start_manager(coord: Arc<Coordinator>) -> 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!(

View file

@ -111,7 +111,7 @@ pub async fn ensure_root_agent(coord: &Arc<Coordinator>) -> 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<Coordinator>) -> 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?;