diff --git a/hive-c0re/src/actions.rs b/hive-c0re/src/actions.rs index fc18d29..bfa7904 100644 --- a/hive-c0re/src/actions.rs +++ b/hive-c0re/src/actions.rs @@ -261,6 +261,14 @@ pub async fn destroy(coord: &Coordinator, name: &str, purge: bool) -> Result<()> } } } + // Meta flake: drop the agent's input + nixosConfiguration so a + // future spawn under the same name re-seeds cleanly, and so the + // meta lock doesn't reference a vanished applied repo. Log + keep + // going on failure — destroy already succeeded at the + // nixos-container level, the meta repo is just bookkeeping. + if let Err(e) = sync_meta_after_lifecycle(coord).await { + tracing::warn!(error = ?e, %name, "meta sync after destroy failed"); + } let _ = coord.approvals.fail_pending_for_agent( name, if purge { @@ -276,6 +284,14 @@ pub async fn destroy(coord: &Coordinator, name: &str, purge: bool) -> Result<()> Ok(()) } +/// Rerender the meta flake from whatever containers still exist on +/// disk. Called after lifecycle ops that change the agent set (today: +/// destroy). Idempotent — a no-op when nothing changed. +async fn sync_meta_after_lifecycle(coord: &Coordinator) -> Result<()> { + let agents = lifecycle::agents_for_meta_listing().await?; + crate::meta::sync_agents(&coord.hyperhive_flake, coord.dashboard_port, &agents).await +} + pub async fn deny(coord: &Coordinator, id: i64, note: Option<&str>) -> Result<()> { let approval = coord.approvals.get(id)?; coord.approvals.mark_denied(id, note)?; diff --git a/hive-c0re/src/lifecycle.rs b/hive-c0re/src/lifecycle.rs index 93e6bdf..1004c45 100644 --- a/hive-c0re/src/lifecycle.rs +++ b/hive-c0re/src/lifecycle.rs @@ -213,6 +213,14 @@ async fn agents_after_spawn(name: &str) -> Result> { agents_for_meta(Some(name)).await } +/// Public enumeration of currently-existing agents (whatever +/// `nixos-container list` says), sorted, no extras. For callers +/// outside this module that need to reseed meta after lifecycle +/// changes — destroy, startup reconciliation, etc. +pub async fn agents_for_meta_listing() -> Result> { + agents_for_meta(None).await +} + pub async fn kill(name: &str) -> Result<()> { validate(name)?; let container = container_name(name);