diff --git a/hive-c0re/src/lifecycle/git.rs b/hive-c0re/src/lifecycle/git.rs index fa35696c..a54299d9 100644 --- a/hive-c0re/src/lifecycle/git.rs +++ b/hive-c0re/src/lifecycle/git.rs @@ -47,10 +47,17 @@ pub(super) async fn git_commit(dir: &Path, message: &str) -> Result<()> { /// Spawn `git` honoring the `HYPERHIVE_GIT` env var (absolute path baked in /// by the NixOS module), falling back to bare `git` (PATH lookup) otherwise. +/// +/// `kill_on_drop(true)`: if the caller's future is dropped before the child +/// exits — e.g. a `tokio::time::timeout` around startup migration fires — the +/// git child is killed instead of orphaned (left retrying an unreachable +/// forge). No-op on normal completion, where the child has already exited. #[must_use] pub fn git_command() -> Command { let exe = std::env::var("HYPERHIVE_GIT").unwrap_or_else(|_| "git".into()); - Command::new(exe) + let mut cmd = Command::new(exe); + cmd.kill_on_drop(true); + cmd } pub async fn git(dir: &Path, args: &[&str]) -> Result<()> { diff --git a/hive-c0re/src/meta.rs b/hive-c0re/src/meta.rs index 7d824d38..1d111c83 100644 --- a/hive-c0re/src/meta.rs +++ b/hive-c0re/src/meta.rs @@ -1391,6 +1391,10 @@ async fn nix_output(dir: &Path, args: &[&str]) -> Result { Command::new("nix") .current_dir(dir) .args(nix_argv(args)) + // Kill the nix child if the caller's future is dropped (e.g. the + // startup-migration timeout around `sync_agents` fires) rather than + // orphaning it against an unreachable forge. No-op on normal exit. + .kill_on_drop(true) .output() .await .with_context(|| format!("nix {} in {}", args.join(" "), dir.display()))