agents: persistent /agents/<n>/work dir outside state/
Agents keep git worktrees and cargo target dirs under /home/<agent>, which lives in the container rootfs and is deleted on destroy. They can't move them under state/, because a claude started there walks up into state/CLAUDE.md and takes on the agent's identity. Add a per-agent work/ dir next to state/ on the host (/var/lib/hyperhive/agents/<n>/work), bind-mounted RW at /agents/<n>/work. hive-c0re creates it in ensure_state_dir and in set_nspawn_flags (the rebuild path reaches only the latter), the agent's activation chowns it to the agent user like state/ and harness/, and measure_agent_disk counts it. destroy never touches the agent root, so work/ survives it; purge removes the whole root, so work/ goes with it. Closes #4977
This commit is contained in:
parent
962c1548eb
commit
89a5d65119
8 changed files with 123 additions and 36 deletions
|
|
@ -139,11 +139,20 @@ async fn du_bytes(path: &std::path::Path) -> Option<u64> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Total per-agent disk: the agent's host-side state dir
|
||||
/// (`/var/lib/hyperhive/agents/<name>/state`) plus the container's writable
|
||||
/// rootfs (`/var/lib/nixos-containers/h-<name>`). Each measured with `du -sxb`
|
||||
/// so the shared nix store and other bind mounts are excluded. A path that
|
||||
/// doesn't exist contributes 0.
|
||||
/// Host paths summed into an agent's disk usage: the agent's host-side
|
||||
/// state and work dirs (`/var/lib/hyperhive/agents/<name>/{state,work}`) plus
|
||||
/// the container's writable rootfs (`/var/lib/nixos-containers/h-<name>`).
|
||||
fn agent_disk_paths(name: &hive_types::Ident) -> [PathBuf; 3] {
|
||||
[
|
||||
Coordinator::agent_notes_dir(name),
|
||||
Coordinator::agent_work_dir(name),
|
||||
PathBuf::from(format!("{NIXOS_CONTAINERS_ROOT}/h-{name}")),
|
||||
]
|
||||
}
|
||||
|
||||
/// Total per-agent disk over [`agent_disk_paths`]. Each path is measured
|
||||
/// with `du -sxb` so the shared nix store and other bind mounts are
|
||||
/// excluded. A path that doesn't exist contributes 0.
|
||||
///
|
||||
/// The state dir is resolved via [`Coordinator::agent_notes_dir`] — the same
|
||||
/// host path the dashboard reads agent state from. Hardcoding the
|
||||
|
|
@ -153,10 +162,8 @@ async fn du_bytes(path: &std::path::Path) -> Option<u64> {
|
|||
/// (almost everything is bind-mounted), that surfaced as all agents reporting
|
||||
/// 0 disk.
|
||||
async fn measure_agent_disk(name: &hive_types::Ident) -> u64 {
|
||||
let state_dir = Coordinator::agent_notes_dir(name);
|
||||
let rootfs = PathBuf::from(format!("{NIXOS_CONTAINERS_ROOT}/h-{name}"));
|
||||
let mut total = 0u64;
|
||||
for path in [state_dir, rootfs] {
|
||||
for path in agent_disk_paths(name) {
|
||||
if let Some(bytes) = du_bytes(&path).await {
|
||||
total = total.saturating_add(bytes);
|
||||
}
|
||||
|
|
@ -356,6 +363,19 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disk_paths_cover_state_work_and_rootfs() {
|
||||
let name = hive_types::Ident::parse("iris").expect("valid ident");
|
||||
assert_eq!(
|
||||
agent_disk_paths(&name),
|
||||
[
|
||||
Coordinator::agent_notes_dir(&name),
|
||||
Coordinator::agent_work_dir(&name),
|
||||
PathBuf::from(format!("{NIXOS_CONTAINERS_ROOT}/h-iris")),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn du_bytes_missing_path_is_none() {
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
|
|
|
|||
Loading…
Reference in a new issue