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
1 changed files with 153 additions and 1828 deletions
|
|
@ -1,77 +1,34 @@
|
|||
//! Loose-ends aggregator. Walks the `approvals` table once per call and
|
||||
//! assembles a `Vec<LooseEnd>` for a single agent (`for_agent`) — always
|
||||
//! the caller's own. `Request::GetLooseEnds` from either the agent or
|
||||
//! manager socket lands here so the age-seconds derivation stays in one
|
||||
//! place. Reminders are agent-local (in-container
|
||||
//! `hive-agent::reminders` store) and never sourced from here. The
|
||||
//! `ask`/`answer` MCP tools, their wire protocol (`hive-c0re::questions`,
|
||||
//! `stores::operator_questions`), and the operator dashboard's questions
|
||||
//! pane have all been removed entirely — this file never had a
|
||||
//! `Question` loose-end path to begin with (it only ever emitted
|
||||
//! `PendingMessages`/`Approval`), so nothing here changed shape when
|
||||
//! that removal landed.
|
||||
//! Loose-ends aggregator. Assembles a `Vec<LooseEnd>` for a single agent
|
||||
//! (`for_agent`) — always the caller's own. `Request::GetLooseEnds` from
|
||||
//! either the agent or manager socket lands here. Reminders and todos are
|
||||
//! agent-local (in-container `hive-agent` stores) and never sourced from
|
||||
//! here.
|
||||
//!
|
||||
//! Call frequency is low (an agent doing self-introspection between
|
||||
//! turns), so the sweep happens fresh every time — no caching, no
|
||||
//! mutation events. If the sweep ever shows up in a profile, the sqlite
|
||||
//! queries already filter on the same index (`idx_approvals_pending`)
|
||||
//! that the dashboard uses, so the bottleneck would be json
|
||||
//! (de)serialisation, not the read.
|
||||
//! mutation events.
|
||||
|
||||
use anyhow::Result;
|
||||
use chrono::Utc;
|
||||
use hive_sh4re::inbox::{LooseEnd, saturating_age};
|
||||
use hive_sh4re::inbox::LooseEnd;
|
||||
|
||||
use crate::coordinator::Coordinator;
|
||||
|
||||
/// Open threads pending against `agent`:
|
||||
/// - undelivered inbox messages this agent still owes itself a `recv`
|
||||
/// for (only when the count is non-zero);
|
||||
/// - pending approvals where this agent is the submitter (a parent
|
||||
/// agent with the `approvals` group submits for its children; the
|
||||
/// root submits for top-level agents). Legacy rows with no recorded
|
||||
/// submitter count as the root's.
|
||||
///
|
||||
/// Ordered `pending_messages` (when non-zero) → approvals within the
|
||||
/// returned vector. Within each kind, source-of-truth ordering (sqlite's
|
||||
/// `pending()` query returns newest-first within its index).
|
||||
/// Open threads pending against `agent`: undelivered inbox messages this
|
||||
/// agent still owes itself a `recv` for, only when the count is non-zero.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Propagates errors from `count_pending` and the pending-approval
|
||||
/// sqlite query.
|
||||
/// Propagates errors from `count_pending`.
|
||||
pub fn for_agent(coord: &Coordinator, agent: &str) -> Result<Vec<LooseEnd>> {
|
||||
let now = Utc::now().timestamp();
|
||||
let mut out = Vec::new();
|
||||
// Undelivered inbox messages this agent still owes itself a `recv`
|
||||
// for. Surfaced first (most actionable) and only when non-zero so a
|
||||
// clean inbox doesn't add noise. The wake-message that drove the
|
||||
// current turn is already delivered, so this counts only what's
|
||||
// genuinely still queued.
|
||||
// Only emitted when non-zero so a clean inbox doesn't add noise. The
|
||||
// wake-message that drove the current turn is already delivered, so
|
||||
// this counts only what's genuinely still queued.
|
||||
let pending_messages = coord.broker.count_pending(agent)?;
|
||||
if pending_messages > 0 {
|
||||
out.push(LooseEnd::PendingMessages {
|
||||
count: pending_messages,
|
||||
});
|
||||
}
|
||||
// Show each pending approval to the agent that submitted it. The
|
||||
// submitter column is NULL for rows predating it; those count as
|
||||
// operator-initiated (no agent tracking predates the column).
|
||||
for a in coord.approvals.pending()? {
|
||||
let submitter = coord
|
||||
.approvals
|
||||
.submitter_of(a.id)?
|
||||
.unwrap_or_else(|| "operator".to_owned());
|
||||
if submitter != agent {
|
||||
continue;
|
||||
}
|
||||
out.push(LooseEnd::Approval {
|
||||
id: a.id,
|
||||
agent: a.agent.to_string(),
|
||||
commit_ref: a.commit_ref,
|
||||
description: a.description,
|
||||
age_seconds: saturating_age(now, a.requested_at.timestamp()),
|
||||
});
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue