dashboard: meta flake inputs UI + sequential rebuild loop

new section 'M3T4 1NPUTS' between approvals and message flow:
one row per input in meta/flake.lock (hyperhive first, then
agent-<n> alphabetically). each row shows the input name, the
first 12 chars of the locked sha, a relative timestamp from
locked.lastModified, and the original.url when available.
checkbox per row; submit button is disabled until at least one
box is checked; submitting confirms then POSTs the selected
names to /meta-update.

backend:
- meta::lock_update(inputs: &[String]) — runs 'nix flake update
  <names>' in the meta dir, commits the lock change with a
  combined message ('lock update: hyperhive, agent-coder').
  preserves the existing META_LOCK serialization. existing
  lock_update_for_rebuild / lock_update_hyperhive stay for
  their single-input callers.
- POST /meta-update — comma-separated 'inputs' form field
  (JS joins checkboxes since axum::Form doesn't natively
  decode repeated keys); spawns a background task that runs
  the lock update + per-agent rebuild loop. hyperhive
  selection fans out to all agents; agent-<n> selection only
  rebuilds <n>. each rebuild fires Rebuilt to the manager
  exactly like dashboard / admin-CLI / auto-update.

rebuild loop is sequential — auto_update::run too (was
parallel via tokio::spawn). parallel rebuilds collide on
nix-store's sqlite cache ('sqlite db busy, not using cache')
and the meta META_LOCK contention. nix-daemon serializes the
heavy build steps anyway, so this isn't a throughput loss.
This commit is contained in:
müde 2026-05-16 03:38:07 +02:00
parent 891223219e
commit 266c2c7a77
6 changed files with 331 additions and 18 deletions

View file

@ -182,7 +182,12 @@ pub async fn run(coord: Arc<Coordinator>) -> Result<()> {
}
};
let mut tasks = Vec::new();
// Sequential, one agent at a time. Parallel rebuilds collide on
// nix-store's sqlite cache (the "sqlite db busy, not using
// cache" warning) and also race the meta-lock mutex; the
// resulting log interleave was bad enough on its own. Builds
// serialize on nix-daemon internally anyway, so this isn't a
// throughput loss in practice.
for container in containers {
// Manager and sub-agents share the same lifecycle now; both go
// through rebuild_agent with name-derived paths.
@ -198,17 +203,9 @@ pub async fn run(coord: Arc<Coordinator>) -> Result<()> {
tracing::debug!(%name, "auto-update: up-to-date");
continue;
}
let coord = coord.clone();
let current_rev = current_rev.clone();
tasks.push(tokio::spawn(async move {
if let Err(e) = rebuild_agent(&coord, &name, &current_rev).await {
tracing::warn!(%name, error = ?e, "auto-update: rebuild failed");
}
}));
}
for t in tasks {
let _ = t.await;
if let Err(e) = rebuild_agent(&coord, &name, &current_rev).await {
tracing::warn!(%name, error = ?e, "auto-update: rebuild failed");
}
}
Ok(())
}