feat(#2302): thread &Ident through agent path builders

This commit is contained in:
damocles 2026-07-20 00:22:06 +02:00 committed by mara
commit bf644cc126
23 changed files with 168 additions and 85 deletions

View file

@ -549,12 +549,18 @@ impl Coordinator {
/// created. All other paths are derived statically from `name`.
#[must_use]
pub fn agent_paths(name: &str, agent_dir: PathBuf) -> AgentPaths {
// `name` is validated upstream (spawn-approval / enqueue gate / the
// MANAGER_NAME const), so an invalid ident here is a construction
// bug. This is the step-3 boundary between the Ident-threaded path
// builders and the job_queue layer (threaded post hive-jobq cutover).
let name = hive_host_sock::Ident::parse(name)
.expect("agent_paths: name must be a valid ident (validated at spawn/enqueue)");
AgentPaths {
agent: agent_dir,
proposed: Self::agent_proposed_dir(name),
applied: crate::paths::applied_dir(name),
claude: Self::agent_claude_dir(name),
notes: Self::agent_notes_dir(name),
proposed: Self::agent_proposed_dir(&name),
applied: crate::paths::applied_dir(name.as_str()),
claude: Self::agent_claude_dir(&name),
notes: Self::agent_notes_dir(&name),
}
}
@ -1442,7 +1448,7 @@ impl Coordinator {
/// Manager-editable proposed config repo. Bind-mounted into the manager
/// container as `/agents/<name>/config/`.
pub fn agent_proposed_dir(name: &str) -> PathBuf {
pub fn agent_proposed_dir(name: &hive_host_sock::Ident) -> PathBuf {
crate::paths::agent_state_dir(name).join("config")
}
@ -1450,14 +1456,14 @@ impl Coordinator {
/// container at `/root/.claude` so OAuth state survives container
/// 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 {
pub fn agent_claude_dir(name: &hive_host_sock::Ident) -> PathBuf {
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 {
pub fn agent_notes_dir(name: &hive_host_sock::Ident) -> PathBuf {
crate::paths::agent_state_dir(name).join("state")
}
@ -1467,7 +1473,7 @@ impl Coordinator {
/// `hyperhive-turn-stats.sqlite`, `hyperhive-model`) — kept separate
/// 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 {
pub fn agent_harness_dir(name: &hive_host_sock::Ident) -> PathBuf {
crate::paths::agent_state_dir(name).join("harness")
}
@ -1477,14 +1483,14 @@ impl Coordinator {
/// destroyed-but-kept tombstones; callers filter the latter by
/// subtracting `lifecycle::list()`.
#[must_use]
pub fn kept_state_names() -> Vec<String> {
pub fn kept_state_names() -> Vec<hive_host_sock::Ident> {
let Ok(rd) = std::fs::read_dir(crate::paths::agents_root()) else {
return Vec::new();
};
let mut out: Vec<String> = rd
let mut out: Vec<hive_host_sock::Ident> = rd
.flatten()
.filter(|e| e.file_type().is_ok_and(|t| t.is_dir()))
.filter_map(|e| e.file_name().into_string().ok())
.filter_map(|e| hive_host_sock::Ident::parse(&e.file_name().into_string().ok()?).ok())
.collect();
out.sort();
out
@ -1497,12 +1503,12 @@ impl Coordinator {
/// apply-commit spawns the container. Distinct from tombstones,
/// which have an applied repo from a prior deploy.
#[must_use]
pub fn pending_init_names() -> Vec<String> {
pub fn pending_init_names() -> Vec<hive_host_sock::Ident> {
Self::kept_state_names()
.into_iter()
.filter(|n| {
Self::agent_proposed_dir(n).join(".git").exists()
&& !crate::paths::applied_dir(n).join(".git").exists()
&& !crate::paths::applied_dir(n.as_str()).join(".git").exists()
})
.collect()
}