docs: job-DAG queue model; fold agent_power table into broker.sqlite

coordinator.md rewrites the queue section (node inventory, DAG shapes,
resources, desired-state reconciliation, boot reconcile); approvals.md
+ persistence.md + hivectl --graceful help updated to match. agent_power
lives in broker.sqlite like approvals/questions (own connection + busy
timeout) instead of a separate db file.
This commit is contained in:
müde 2026-07-06 20:27:44 +02:00
commit 604e1c2557
8 changed files with 293 additions and 203 deletions

View file

@ -3,8 +3,11 @@
//! `container_view` remains the observed *status*; the job queue's
//! `Reconcile` nodes are the mechanism that converges the two.
//!
//! Stored in `/var/lib/hyperhive/db/agent_power.sqlite` (one tiny row
//! per agent). Intent persists across hive-c0re restarts; in-flight
//! Stored as the `agent_power` table in the coordinator DB
//! (`/var/lib/hyperhive/db/broker.sqlite`, one tiny row per agent) —
//! same one-file-many-modules pattern as `approvals` /
//! `operator_questions` / `scheduled_prompts`, each with its own
//! connection. Intent persists across hive-c0re restarts; in-flight
//! queue work deliberately does not. Setting `wanted` is never a
//! queued node: operator/intent actions update the row synchronously
//! at request time, then submit the DAG whose terminal `Reconcile`
@ -80,12 +83,21 @@ pub struct PowerStore {
}
impl PowerStore {
pub fn open(db_dir: &Path) -> Result<Self> {
std::fs::create_dir_all(db_dir)
.with_context(|| format!("create agent_power db parent {}", db_dir.display()))?;
let path = db_dir.join("agent_power.sqlite");
let conn = Connection::open(&path)
.with_context(|| format!("open agent_power db {}", path.display()))?;
/// Open (a connection to) the shared coordinator DB and ensure the
/// `agent_power` table exists. `db_path` is the same sqlite file
/// the broker / approvals / questions stores open.
pub fn open(db_path: &Path) -> Result<Self> {
if let Some(parent) = db_path.parent() {
std::fs::create_dir_all(parent)
.with_context(|| format!("create agent_power db parent {}", parent.display()))?;
}
let conn = Connection::open(db_path)
.with_context(|| format!("open agent_power db {}", db_path.display()))?;
// Several modules hold their own connection to this file (the
// broker / approvals / questions pattern); wait out a
// concurrent writer instead of surfacing SQLITE_BUSY.
conn.busy_timeout(std::time::Duration::from_secs(5))
.context("set agent_power busy_timeout")?;
conn.execute_batch(SCHEMA)
.context("apply agent_power schema")?;
Ok(Self {