diff --git a/docs/agent-lifecycle/config-changes.md b/docs/agent-lifecycle/config-changes.md index b31a6dae..7e2a391f 100644 --- a/docs/agent-lifecycle/config-changes.md +++ b/docs/agent-lifecycle/config-changes.md @@ -195,8 +195,8 @@ hive-c0re keeps no copy of an agent's config: the forge's `agent-configs/` record what the agent's last successful build pinned, and nothing else. Older hives also kept an *applied* repo at `applied/` and a *proposed* -clone at `agents//config`. The startup migration removes the applied -repos. Nothing reads the proposed clone any more, and you can delete it. +clone at `agents//config`. Nothing reads either any more, and you can +delete them. The container's `--flake` ref is `internal/meta` (see "Meta flake" above). The agent's `agent-configs//flake.nix` @@ -271,9 +271,6 @@ host set up before the current deploy scheme (described above) converges to it automatically. Each phase is a no-op once already applied: -- **Applied repos**: deletes the per-agent `applied/` repos an earlier hive - kept. The deploy markers beside them stay. Non-destructive for everything - else — it doesn't touch live containers, state dirs, or claude creds. - **Agent files**: refreshes the files that follow the agent set (`agent_set::refresh`) from the current agent list. Set `HIVE_SKIP_META_MIGRATION=1` on the service to defer this phase. diff --git a/docs/agent-lifecycle/persistence.md b/docs/agent-lifecycle/persistence.md index 08686810..87b113c8 100644 --- a/docs/agent-lifecycle/persistence.md +++ b/docs/agent-lifecycle/persistence.md @@ -547,8 +547,8 @@ Under `/var/lib/hyperhive/applied/` — the hive-c0re-only deploy markers `..hyperhive-rev` and `..config-rev`: the hyperhive and config commits the agent's last successful build pinned. No config lives here; the forge's `agent-configs/` is the only copy. A hive that predates this -finds per-agent `applied/` repos here; `migrate::run` deletes them at -startup. +finds per-agent `applied/` repos here. Nothing reads them any more, and +you can delete them. Under `/var/lib/hyperhive/meta/` — system-level config files, plain state with no git history. The flake agents build from is the diff --git a/hive-c0re/src/migrate.rs b/hive-c0re/src/migrate.rs index 05684afd..59932244 100644 --- a/hive-c0re/src/migrate.rs +++ b/hive-c0re/src/migrate.rs @@ -1,5 +1,5 @@ -//! Startup convergence. Three phases, all idempotent and unguarded: -//! harness files, the leftover applied repos, the agent-set files, plus the +//! Startup convergence. Two phases, all idempotent and unguarded: +//! harness files and the agent-set files, plus the //! removal of the legacy meta repo's leftovers. They re-run every //! boot on purpose — each one is a no-op once its state is already //! correct. @@ -72,16 +72,8 @@ pub async fn run() -> Result<()> { Err(e) => tracing::warn!(error = ?e, "migration: removing the legacy meta .git failed"), } - // Phase 1: the applied repos an earlier hive kept per agent. - tracing::debug!("migration: phase 1 (leftover applied repos)"); - match remove_applied_repos(&crate::paths::applied_root()) { - Ok(0) => {} - Ok(n) => tracing::info!(count = n, "migration: removed leftover applied repos"), - Err(e) => tracing::warn!(error = ?e, "migration: removing the applied repos failed"), - } - - // Phase 2: the files that follow the agent set. - tracing::debug!("migration: phase 2 (agent_set refresh)"); + // Phase 1: the files that follow the agent set. + tracing::debug!("migration: phase 1 (agent_set refresh)"); // `refresh` takes the agents it is given as the whole set, so an empty // stand-in for an unreadable list would drop every agent from the topology. let agents = match lifecycle::agents_for_meta_listing().await { @@ -167,29 +159,6 @@ async fn enumerate_agents() -> Result> { .collect()) } -/// Remove the per-agent `applied/` repos an earlier hive kept in `root`, -/// destroyed agents' included. The forge's `agent-configs/` is the only -/// copy of a config now, so nothing reads them. The dot-files beside them (the -/// deploy markers) stay. Returns how many it removed. -fn remove_applied_repos(root: &Path) -> Result { - let entries = match std::fs::read_dir(root) { - Ok(entries) => entries, - Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(0), - Err(e) => return Err(e).with_context(|| format!("reading {}", root.display())), - }; - let mut removed = 0; - for entry in entries { - let entry = entry.with_context(|| format!("reading {}", root.display()))?; - if !entry.file_type()?.is_dir() { - continue; - } - let path = entry.path(); - std::fs::remove_dir_all(&path).with_context(|| format!("removing {}", path.display()))?; - removed += 1; - } - Ok(removed) -} - #[cfg(test)] mod tests { use super::*; @@ -231,30 +200,4 @@ mod tests { assert!(dir.path().join("forge-other").exists(), "neighbour stays"); assert!(!remove_core_token(dir.path()).expect("second"), "no-op"); } - - #[test] - fn applied_repos_go_and_the_markers_beside_them_stay() { - let root = tempfile::tempdir().expect("tempdir"); - for name in ["alice", "bob"] { - std::fs::create_dir_all(root.path().join(name).join(".git")).expect("mkdir repo"); - std::fs::write(root.path().join(name).join("agent.nix"), "{}").expect("write config"); - std::fs::write(root.path().join(format!(".{name}.config-rev")), "abc").expect("marker"); - } - - assert_eq!(remove_applied_repos(root.path()).expect("first"), 2); - assert!(!root.path().join("alice").exists()); - assert!( - root.path().join(".alice.config-rev").exists(), - "marker stays" - ); - assert_eq!( - remove_applied_repos(root.path()).expect("second"), - 0, - "no-op" - ); - assert_eq!( - remove_applied_repos(&root.path().join("missing")).expect("absent root"), - 0 - ); - } }