hive-c0re: remove the approvals subsystem
Nothing submits an approval since scheduled prompts left hive-c0re, so the
queue only acted on rows written before that.
- hive-c0re: the approvals store and its migrations, approve/deny/
resolve actions, the ResolveApproval tail node (and meta_update's
approval_id), POST /api/approve and /api/deny, the approval views on
/api/state, the approval_resolved dashboard event, the orphan GC,
destroy/purge failing pending rows, and the approval rows in
get_loose_ends. DestroyBookkeeping loses the purge flag, which only
worded the approval-failure note.
- The CancelLooseEnd request goes: its only hive-c0re arm was the
approval one. cancel_loose_end handles reminder and todo in the harness.
- Wire types: hive_sh4re::approvals (ReminderStats moves to inbox),
LooseEnd::Approval, CancelLooseEndKind, HelperEvent::ApprovalResolved,
ToolGroup::Approvals, HostRequest::{Pending,Approve,Deny} and
HostResponse.approvals. HOST_SOCK_VERSION goes to 2 for the removed
field.
- hivectl loses the approvals verbs; docs/tools/hivectl-cli.md is
regenerated.
- The system prompt drops approval_resolved.
- The approvals table stays in broker.sqlite, unread, the same way the
operator_questions table was left when its store was removed.
- An `approvals` entry still in tool-groups.json is skipped with a warning
by ToolGroup::parse_list, like any unknown group.
- docs/agent-lifecycle/approvals.md is renamed to config-changes.md so the
code's doc pointers resolve; its content follows in the docs commit.
This commit is contained in:
parent
99bd912ae0
commit
58ef299c3c
60 changed files with 153 additions and 1828 deletions
|
|
@ -10,7 +10,6 @@ use std::sync::{Arc, Mutex};
|
|||
use anyhow::{Context, Result};
|
||||
use tokio::sync::{broadcast, watch};
|
||||
|
||||
use crate::approvals::Approvals;
|
||||
use crate::broker::Broker;
|
||||
use crate::container_view::{self, ContainerView};
|
||||
use crate::dashboard_events::DashboardEvent;
|
||||
|
|
@ -30,7 +29,6 @@ const LAST_STOPPED_RUNNING_KEY: &str = "last_stopped_running";
|
|||
|
||||
pub struct Coordinator {
|
||||
pub broker: Arc<Broker>,
|
||||
pub approvals: Arc<Approvals>,
|
||||
/// Scheduled-prompt rows, read-only: the dashboard lists them so the
|
||||
/// operator can recreate them with `swarmctl schedule`. Nothing on
|
||||
/// the hive writes or fires them.
|
||||
|
|
@ -170,7 +168,7 @@ pub struct Coordinator {
|
|||
/// Unified wire-facing event channel feeding the dashboard SSE
|
||||
/// stream. Carries broker messages (mirrored from `broker.subscribe`
|
||||
/// by the forwarder task in `main.rs`) and dashboard-only mutation
|
||||
/// events (approval added/resolved, etc.).
|
||||
/// events (container state, rebuild queue, etc.).
|
||||
/// Snapshot endpoints capture `event_seq` before reading state so
|
||||
/// the client can dedupe its buffered live traffic against the
|
||||
/// snapshot.
|
||||
|
|
@ -413,19 +411,6 @@ impl Drop for MetaUpdateGuard {
|
|||
}
|
||||
}
|
||||
|
||||
/// Field-named payload for [`Coordinator::emit_approval_resolved`].
|
||||
/// Mirrors the `ApprovalResolved` dashboard-event fields. `agent`
|
||||
/// borrows from the caller; `approval_kind` / `status` are
|
||||
/// compile-time constants.
|
||||
pub struct ApprovalResolved<'a> {
|
||||
pub id: i64,
|
||||
pub agent: &'a str,
|
||||
pub approval_kind: &'static str,
|
||||
pub status: &'static str,
|
||||
pub note: Option<String>,
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
/// The two ways [`Coordinator::set_paused_by_name`] can fail. See its own
|
||||
/// doc comment for why this is a distinct type rather than one
|
||||
/// `anyhow::Error` (`Write` already wraps one, `BadName` never needs to).
|
||||
|
|
@ -467,7 +452,6 @@ impl Coordinator {
|
|||
agent_io_weight,
|
||||
} = env;
|
||||
let broker = Broker::open(db_path).context("open broker")?;
|
||||
let approvals = Approvals::open(db_path).context("open approvals")?;
|
||||
let scheduled_prompts = crate::scheduled_prompts::ScheduledPrompts::open(db_path)
|
||||
.context("open scheduled_prompts")?;
|
||||
// BuildLogs wants a directory (it picks its own `build_logs.sqlite`
|
||||
|
|
@ -488,7 +472,6 @@ impl Coordinator {
|
|||
let (shutdown_tx, _) = watch::channel(false);
|
||||
Ok(Self {
|
||||
broker: Arc::new(broker),
|
||||
approvals: Arc::new(approvals),
|
||||
scheduled_prompts: Arc::new(scheduled_prompts),
|
||||
build_logs,
|
||||
hyperhive_flake,
|
||||
|
|
@ -730,41 +713,6 @@ impl Coordinator {
|
|||
self.meta_updates_active.load(Ordering::SeqCst) > 0
|
||||
}
|
||||
|
||||
/// Emit `ApprovalResolved` after `mark_approved` / `mark_denied` /
|
||||
/// `mark_failed` lands. `resolved_at` is stamped from the system
|
||||
/// clock here so call sites don't repeat the conversion; if you
|
||||
/// already have an authoritative timestamp from the db update,
|
||||
/// the tiny skew between "row updated" and "event emitted" is
|
||||
/// presentation-only and doesn't matter to clients.
|
||||
///
|
||||
/// Takes [`ApprovalResolved`] rather than a positional arg list so
|
||||
/// the seven fields are named at every call site.
|
||||
pub fn emit_approval_resolved(&self, ev: ApprovalResolved<'_>) {
|
||||
let ApprovalResolved {
|
||||
id,
|
||||
agent,
|
||||
approval_kind,
|
||||
status,
|
||||
note,
|
||||
description,
|
||||
} = ev;
|
||||
let resolved_at = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.ok()
|
||||
.and_then(|d| i64::try_from(d.as_secs()).ok())
|
||||
.unwrap_or(0);
|
||||
self.emit_dashboard_event(DashboardEvent::ApprovalResolved {
|
||||
seq: self.next_seq(),
|
||||
id,
|
||||
agent: agent.to_owned(),
|
||||
approval_kind,
|
||||
status,
|
||||
resolved_at: hive_sh4re::wire_time::from_secs(resolved_at),
|
||||
note,
|
||||
description,
|
||||
});
|
||||
}
|
||||
|
||||
/// Rebuild the per-container snapshot, diff it against the last
|
||||
/// one cached on `self`, and emit one
|
||||
/// `DashboardEvent::ContainerStateChanged` per added/changed row
|
||||
|
|
@ -844,7 +792,7 @@ impl Coordinator {
|
|||
.with_context(|| format!("create agent dir {}", agent_dir.display()))?;
|
||||
let socket_path = Self::socket_path(name);
|
||||
// Hand the full Coordinator to the per-agent socket — it needs
|
||||
// more than just the broker (approvals, ...).
|
||||
// more than just the broker.
|
||||
let socket = socket_server::start(name, &socket_path, self.clone())?;
|
||||
self.agents.lock().unwrap().insert(name.to_owned(), socket);
|
||||
Ok(agent_dir)
|
||||
|
|
@ -1094,28 +1042,6 @@ impl Coordinator {
|
|||
self.notify_agent(hive_sh4re::manager::MANAGER_AGENT, event);
|
||||
}
|
||||
|
||||
/// Route an approval-scoped helper event to the agent that submitted
|
||||
/// approval `approval_id` (the authenticated socket caller at submit
|
||||
/// time). Legacy rows with no recorded submitter — and any lookup
|
||||
/// failure — fall back to the root agent, preserving the prior
|
||||
/// always-root behaviour.
|
||||
pub fn notify_submitter(&self, approval_id: i64, event: &hive_sh4re::manager::HelperEvent) {
|
||||
let target = self.submitter_or_manager(approval_id);
|
||||
self.notify_agent(&target, event);
|
||||
}
|
||||
|
||||
/// Shared resolution for "which agent should hear about approval
|
||||
/// `approval_id`" — the authenticated socket caller at submit time,
|
||||
/// falling back to the manager for legacy rows with no recorded
|
||||
/// submitter (or any lookup failure). Used by both `notify_submitter`
|
||||
/// and `push_todo_submitter` so the fallback rule lives in one place.
|
||||
fn submitter_or_manager(&self, approval_id: i64) -> String {
|
||||
self.approvals
|
||||
.submitter_of(approval_id)
|
||||
.unwrap_or_default()
|
||||
.unwrap_or_else(|| hive_sh4re::manager::MANAGER_AGENT.to_owned())
|
||||
}
|
||||
|
||||
/// Push a `HelperEvent` into an arbitrary agent's inbox. Encoded
|
||||
/// the same way as `notify_manager` (sender = `SYSTEM_SENDER`,
|
||||
/// body = JSON-encoded event) — e.g. `ContainerCrash`, `NeedsUpdate`.
|
||||
|
|
|
|||
Loading…
Reference in a new issue