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
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
use super::model::NodeKind;
|
||||
use super::*;
|
||||
use hive_jobq::TerminalState;
|
||||
|
||||
/// Insert a declared job, naming nothing — the shape assertions read the whole
|
||||
/// graph. A test that needs a handle calls `q.insert` directly and names the
|
||||
|
|
@ -101,10 +102,9 @@ struct Declared {
|
|||
|
||||
/// Render a dep's outcome set as the outcomes it actually accepts.
|
||||
///
|
||||
/// ⚠️ Spelled out rather than bucketed into `ok` / `any` / other. The first
|
||||
/// version of this did bucket, and a template's three `ResolveApproval` tails —
|
||||
/// which differ ONLY in their accepted outcomes — all rendered as `"other"`.
|
||||
/// A helper that prints two structurally different nodes identically turns an
|
||||
/// ⚠️ Spelled out rather than bucketed into `ok` / `any` / other: tails that
|
||||
/// differ ONLY in their accepted outcomes would render identically, and a
|
||||
/// helper that prints two structurally different nodes identically turns an
|
||||
/// assertion into a tautology.
|
||||
fn when_tag(when: hive_jobq::DepWhen) -> String {
|
||||
[
|
||||
|
|
@ -194,19 +194,6 @@ fn pending_kinds_for(q: &JobQueue, agent: &str) -> Vec<&'static str> {
|
|||
pending_kinds_filtered(q, &|kind: &NodeKind| kind.agent() == agent)
|
||||
}
|
||||
|
||||
/// The payloads of every node still `Pending`, for the cases where *which* of a
|
||||
/// family of same-kind nodes survived is the assertion — a template emits one
|
||||
/// tail per outcome and they differ only in what they carry.
|
||||
fn pending_payloads(q: &JobQueue) -> Vec<NodeKind> {
|
||||
let sched = q.sched().lock().expect("job_queue mutex poisoned");
|
||||
let graph = sched.graph();
|
||||
graph
|
||||
.nodes()
|
||||
.filter(|n| n.state == State::Pending)
|
||||
.map(|n| n.payload.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn pending_kinds_filtered(q: &JobQueue, keep: &dyn Fn(&NodeKind) -> bool) -> Vec<&'static str> {
|
||||
let sched = q.sched().lock().expect("job_queue mutex poisoned");
|
||||
let graph = sched.graph();
|
||||
|
|
@ -1189,43 +1176,6 @@ fn cancelled_power_op_runs_no_compensating_node() {
|
|||
}
|
||||
}
|
||||
|
||||
// ---- terminal reporting + lease release ----
|
||||
|
||||
/// A DAG cancelled while fully queued must still **run its tail**, or a queued
|
||||
/// approval DAG cancelled by the operator would dangle its approval forever.
|
||||
///
|
||||
/// This is the load-bearing case for sparing tails in [`JobQueue::cancel`]: the
|
||||
/// work nodes all cancel, but `ResolveApproval` is weak-edged, so a `Cancelled`
|
||||
/// dep satisfies its edge and it becomes claimable instead of being cancelled
|
||||
/// along with everything else. It reads `Cancelled` off its own deps and resolves
|
||||
/// the approval as "cancelled before completion".
|
||||
#[test]
|
||||
fn cancelled_dag_still_runs_its_approval_tail() {
|
||||
let q = JobQueue::new(1);
|
||||
insert(&q, |builder| {
|
||||
templates::meta_update(builder, Vec::new(), Some(7));
|
||||
});
|
||||
// Found by kind, not returned: `meta_update` names nothing, because the
|
||||
// approval row is how an operator follows it.
|
||||
let lock = node_of(&q, "meta_lock").get();
|
||||
assert!(q.cancel(lock), "fully-queued dag cancels");
|
||||
// The `Cancelled` tail is the only node whose edge accepts a dropped
|
||||
// dependency, so it is the only one `cancel` spares — and *which* tail
|
||||
// survives names how the approval row is about to be resolved.
|
||||
let spared = pending_payloads(&q);
|
||||
assert!(
|
||||
matches!(
|
||||
spared.as_slice(),
|
||||
[NodeKind::ResolveApproval {
|
||||
approval_id: 7,
|
||||
outcome: TerminalState::Cancelled
|
||||
}]
|
||||
),
|
||||
"only the cancelled-outcome tail is spared, got {spared:?}"
|
||||
);
|
||||
assert_eq!(state_of(&q, lock), State::Cancelled);
|
||||
}
|
||||
|
||||
// ---- history ----
|
||||
//
|
||||
// The node → build-log link is no longer queue state: the log row carries
|
||||
|
|
|
|||
Loading…
Reference in a new issue