phase 8 step 2: approval-gated spawn + dashboard spinner

This commit is contained in:
müde 2026-05-15 12:53:13 +02:00
parent a42fdb3a5c
commit c59fa8541c
10 changed files with 382 additions and 90 deletions

View file

@ -30,6 +30,24 @@ pub struct Coordinator {
/// `flake.nix` files as `inputs.hyperhive.url`.
pub hyperhive_flake: String,
agents: Mutex<HashMap<String, AgentSocket>>,
/// Agents whose lifecycle action (currently just spawn) is in flight.
/// Read by the dashboard to render a spinner; cleared when the action
/// resolves (success or failure).
transient: Mutex<HashMap<String, TransientState>>,
}
/// Per-agent in-progress state that the dashboard surfaces between approve
/// click and container ready.
#[derive(Debug, Clone)]
pub struct TransientState {
pub kind: TransientKind,
pub since: std::time::Instant,
}
#[derive(Debug, Clone, Copy)]
pub enum TransientKind {
/// `lifecycle::spawn` is running (nixos-container create + update + start).
Spawning,
}
impl Coordinator {
@ -41,6 +59,7 @@ impl Coordinator {
approvals: Arc::new(approvals),
hyperhive_flake,
agents: Mutex::new(HashMap::new()),
transient: Mutex::new(HashMap::new()),
})
}
@ -64,6 +83,25 @@ impl Coordinator {
}
}
/// Mark an agent as in-progress (only one state per agent for now).
pub fn set_transient(&self, name: &str, kind: TransientKind) {
self.transient.lock().unwrap().insert(
name.to_owned(),
TransientState {
kind,
since: std::time::Instant::now(),
},
);
}
pub fn clear_transient(&self, name: &str) {
self.transient.lock().unwrap().remove(name);
}
pub fn transient_snapshot(&self) -> HashMap<String, TransientState> {
self.transient.lock().unwrap().clone()
}
pub fn agent_dir(name: &str) -> PathBuf {
PathBuf::from(format!("{AGENT_RUNTIME_ROOT}/{name}"))
}