From 22f35def8fd9e5b32bdcbb2135513dc7107c4a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?m=C3=BCde?= Date: Sat, 16 May 2026 00:29:26 +0200 Subject: [PATCH] actions::destroy syncs meta after lifecycle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit once nixos-container destroy lands + per-agent state cleanup is done, rerender the meta flake from the remaining containers so the destroyed agent's input + nixosConfiguration drop off and its flake.lock entry vanishes. log + keep going on meta-sync failure — the destroy already succeeded at the lifecycle level, so meta drift here is just bookkeeping. new public lifecycle::agents_for_meta_listing exposes the agent enumeration for callers outside the module. --- hive-c0re/src/actions.rs | 16 ++++++++++++++++ hive-c0re/src/lifecycle.rs | 8 ++++++++ 2 files changed, 24 insertions(+) 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);